/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "nas_share/common/HetroCommonService.h"
#include <boost/filesystem.hpp>
#include "common/EnvVarManager.h"
#include "utils/PluginUtilities.h"
#include "system/System.hpp"
#include "client/ClientInvoke.h"
#include "utils/CertMgr.h"
#include "log/BackupFailureRecorder.h"
#include "security/cmd/CmdParam.h"
#include "security/cmd/CmdExecutor.h"
#include "job/ChannelManager.h"

namespace {
    constexpr auto MODULE = "HetroCommonService";
    const int NUM_TEN = 10;
    const std::string PLUGIN_ATT_JSON = "plugin_attribute_1.0.0.json";
    const std::string DEFAULT_FAILURE_DEFAULT_ROOT =
        R"(/opt/logpath/logs)";
    const std::string NAS_SHARE_STR = "NasShare";
    const std::string NAS_FILESYSTEM_STR = "NasFileSystem";
    int NAS_SCANNER_MAX_SIZE = 5;
}
using namespace PluginUtils;
using namespace std;
using namespace Module;
using namespace NasChannelManager;

NodeLevelTaskInfo g_nodeLevelTaskInfo {};
std::mutex HetroCommonService::m_pluginAtrributeJsonFileMutex {};

HetroCommonService::HetroCommonService() : m_scanMgr(ScanMgr::GetInstance())
{
    /* use /opt/logpath/log records output root */
    m_failureRecordRoot = DEFAULT_FAILURE_DEFAULT_ROOT;
    m_maxFailureRecordsNum = DEFAULT_MAX_FAILURE_RECORDS_NUM;
    NAS_SCANNER_MAX_SIZE = Module::ConfigReader::getInt(DME_NAS_CONFIG_SECTION,
        "DME_NAS_SCANNER_MAX_SIZE");
}

bool HetroCommonService::checkFilePathAndGetSrcFileList(std::string srcDir, std::string dstDir,
    std::vector<std::string> &srcFileList)
{
    if (!PathExistMandate(srcDir)) {
        HCP_Log(ERR, MODULE) << "SrcDir does not exist: " << srcDir << HCPENDLOG;
        return false;
    }

    if (!PathExistMandate(dstDir)) {
        HCP_Log(ERR, MODULE) << "DstDir does not exist: " << dstDir << HCPENDLOG;
        return false;
    }

    if (!GetFileListInDirectory(srcDir, srcFileList)) {
        HCP_Log(ERR, MODULE) << "Get filelist for dir failed: " << srcDir << HCPENDLOG;
        return false;
    }

    return true;
}

bool HetroCommonService::IsValidCtrlFile(uint32_t subTaskType, std::string ctrlFileFullPath)
{
    /* "txt.tmp" are temproary files and is considered as not valid for all the phases */
    if (ctrlFileFullPath.find("txt.tmp") != std::string::npos) {
        return false;
    }

    switch (subTaskType) {
        case SUBJOB_TYPE_DATACOPY_COPY_PHASE: {
            return ctrlFileFullPath.find("/control_") != std::string::npos;
        }
        case SUBJOB_TYPE_DATACOPY_DELETE_PHASE: {
            return ctrlFileFullPath.find("/delete_control_") != std::string::npos;
        }
        case SUBJOB_TYPE_DATACOPY_HARDLINK_PHASE: {
            return ctrlFileFullPath.find("/hardlink_control_") != std::string::npos;
        }
        case SUBJOB_TYPE_DATACOPY_DIRMTIME_PHASE: {
            return ctrlFileFullPath.find("/mtime_") != std::string::npos;
        }
        case SUBJOB_TYPE_ANY: {
            return (ctrlFileFullPath.find("/control_") != std::string::npos) ||
                (ctrlFileFullPath.find("/delete_control_") != std::string::npos) ||
                (ctrlFileFullPath.find("/hardlink_control_") != std::string::npos) ||
                (ctrlFileFullPath.find("/mtime_") == std::string::npos);
        }
        default: {
            return false;
        }
    }
}

bool HetroCommonService::GetSubJobTypeByFileName(const std::string &fileName, std::string &subTaskName,
    uint32_t &subTaskType, uint32_t &subTaskPrio)
{
    if (fileName.find("hardlink") != std::string::npos) {
        subTaskName = "NasBackup_HardlinkCtrlFile_" + GetUniqueId();
        subTaskType = SUBJOB_TYPE_DATACOPY_HARDLINK_PHASE;
        subTaskPrio = SUBJOB_TYPE_DATACOPY_HARDLINK_PHASE_PRIO;
    } else if (fileName.find("mtime") != std::string::npos) {
        subTaskName = "NasBackup_DirMtimeFile_" + GetUniqueId();
        subTaskType = SUBJOB_TYPE_DATACOPY_DIRMTIME_PHASE;
        subTaskPrio = SUBJOB_TYPE_DATACOPY_DIRMTIME_PHASE_PRIO;
    } else if (fileName.find("delete") != std::string::npos) {
        subTaskName = "NasBackup_DelFile_" + GetUniqueId();
        subTaskType = SUBJOB_TYPE_DATACOPY_DELETE_PHASE;
        subTaskPrio = SUBJOB_TYPE_DATACOPY_DELETE_PHASE_PRIO;
    } else if (fileName.find("/control_") != std::string::npos) {
        subTaskName = "NasBackup_CtrlFile_" + GetUniqueId();
        subTaskType = SUBJOB_TYPE_DATACOPY_COPY_PHASE;
        subTaskPrio = SUBJOB_TYPE_DATACOPY_COPY_PHASE_PRIO;
    }
    if (m_aggregateRestore) {
        subTaskPrio = m_orderNumberForAggregate * SUBJOB_TYPE_AGGREGATE_BASE_PRIO + subTaskPrio;
    }
    return true;
}

bool HetroCommonService::InitSubTask(SubJob &subJob, const std::string& jobId,
    const std::string& ctrlFile, uint32_t &subTaskType, const std::string& ext, const std::string& parentDir)
{
    std::string subTaskName {};
    uint32_t subTaskPrio {};
    std::string backupSubJobStr {};
    HetroBackupSubJob backupSubJob {};
    GetSubJobTypeByFileName(ctrlFile, subTaskName, subTaskType, subTaskPrio);
    backupSubJob.m_ControlFile = ctrlFile;
    backupSubJob.m_SubTaskType = subTaskType;
    backupSubJob.m_Ext = ext;
    backupSubJob.m_parentDir = parentDir;
    if (!Module::JsonHelper::StructToJsonString(backupSubJob, backupSubJobStr)) {
        HCP_Log(ERR, MODULE) << "Convert to json failed for file: "
        << WIPE_SENSITIVE(backupSubJob.m_ControlFile) << HCPENDLOG;
        return false;
    }
    subJob.__set_jobId(jobId);
    subJob.__set_jobName(subTaskName);
    subJob.__set_jobType(SubJobType::BUSINESS_SUB_JOB);
    subJob.__set_policy(ExecutePolicy::ANY_NODE);
    subJob.__set_jobInfo(backupSubJobStr);
    subJob.__set_jobPriority(subTaskPrio);
    subJob.__set_ignoreFailed(false);
    return true;
}

int HetroCommonService::CreateSubTask(std::vector<SubJob> &subJobList, std::vector<std::string> &ctrlFileList)
{
    if (subJobList.empty() || ctrlFileList.empty()) {
        DBGLOG("subJobList or ctrlFileList are empty, exit CreateSubTask");
        return Module::SUCCESS;
    }
    INFOLOG("Enter CreateSubTask, jobId: %s subJobList size = %d", m_jobId.c_str(), subJobList.size());
    for (const SubJob& subJob: subJobList) {
        HCP_Log(INFO, MODULE)
            << "Create subtask, jobId: " << subJob.jobId
            << ", jobName: " << subJob.jobName << ", jobType: " << subJob.jobType
            << ", jobPrio: " << subJob.jobPriority << ", jobInfo: " << subJob.jobInfo
            << ", time: " << PluginUtils::GetCurrentTimeInSeconds() << HCPENDLOG;
    }
    ActionResult ret;
    int retryCnt = 0;
    do {
        if (retryCnt == MIDDLE_NORMAL_RETRY_TIMES) {
            HetroCommonService::KeepJobAlive(m_jobId, m_subJobId);
        }
        if (IsAbortJob()) {
            HCP_Log(WARN, MODULE) << "Abort Recvied taskid: " << m_jobId << ", subtaskid: " << m_subJobId << HCPENDLOG;
            break;
        }
        InterfaceInvokeTimer(
            std::chrono::seconds(NUM_TEN),
            [&](...) { JobService::AddNewJob(ret, subJobList); },
            [&](...) { WARNLOG("JobService::AddNewJob timeout!"); });
        if (ret.code == Module::SUCCESS) {
            DBGLOG("Exit AddNewJob, success, jobId: %s", m_jobId.c_str());
            break;
        } else if ((ret.bodyErr == RETRY_ERR_CODE) && (ret.code == Module::INTERNAL_ERR)) {
            WARNLOG("Exit AddNewJob, retry, jobId: %s retCode: %u", m_jobId.c_str(), ret.code);
            return Module::RETRY;
        }
        sleep(NORMAL_RETRY_INTERVAL);
        WARNLOG("AddNewJob for main task: %s failed: retCode: %u", m_jobId.c_str(), ret.code);
    } while (++retryCnt <= NORMAL_RETRY_TIMES);
    if (ret.code != MP_SUCCESS) {
        ERRLOG("Exit AddNewJob, failed after max retry, ret.code: %d, jobId: %s", ret.code, m_jobId.c_str());
        return Module::FAILED;
    }
    for (const std::string& ctrlFile : ctrlFileList) {
        RemoveFile(ctrlFile);
        g_nodeLevelTaskInfo.IncrSubTasksSubmitCount(); // Increment the task submit count
    }
    subJobList.clear();
    ctrlFileList.clear();
    DBGLOG("Exit CreateSubTasksFromCtrlFile, CreateSubTask");
    return Module::SUCCESS;
}

int HetroCommonService::CreateSubTask(const SubJob &subJob)
{
    HCP_Log(INFO, MODULE)
        << "Create subtask, jobId: " << subJob.jobId
        << ", jobName: " << subJob.jobName
        << ", jobType: " << subJob.jobType
        << ", jobPrio: " << subJob.jobPriority
        << ", jobInfo: " << subJob.jobInfo
        // << ", time: " << GetCurrentTimeInSeconds()
        << HCPENDLOG;
    std::vector<SubJob> subJobList;
    subJobList.push_back(subJob);
    ActionResult ret;
    int retryCnt = 0;
    do {
        if (IsAbortJob()) {
            INFOLOG("Exit received Abort for taskid: %s, subtaskid: %s", m_jobId.c_str(), m_subJobId.c_str());
            return Module::SUCCESS;
        }
        JobService::AddNewJob(ret, subJobList);
        if (ret.code == Module::SUCCESS) {
            DBGLOG("Create SubTask success, jobId: %s", m_jobId.c_str());
            return Module::SUCCESS;
        } else if ((ret.bodyErr == RETRY_ERR_CODE) && (ret.code == Module::INTERNAL_ERR)) {
            DBGLOG("Exit AddNewJob, retry %d, jobId: %s", retryCnt, m_jobId.c_str());
            return Module::RETRY;
        }
        sleep(NORMAL_RETRY_INTERVAL);
        WARNLOG("Create SubTask failed for main task: %s failed: %u bodyErr: %u",
            m_jobId.c_str(), ret.code, ret.bodyErr);
    } while (++retryCnt <= NORMAL_RETRY_TIMES);
    ERRLOG("Create SubTask failed after max retry, ret.code:%d, jobId: %s", ret.code, m_jobId.c_str());
    return Module::FAILED;
}

bool HetroCommonService::ReportJobDetails(const SubJobDetails &jobInfo)
{
    ActionResult ret;
    HCP_Log(INFO, MODULE) << "ReportJobDetails "
        << ", jobId: " << jobInfo.jobId
        << ", subJobId: " << jobInfo.subJobId
        << ", jobStatus: " << jobInfo.jobStatus
        << ", progress: " << jobInfo.progress
        << ", dataSize: " << jobInfo.dataSize
        << ", level: " << jobInfo.logDetail[0].level
        << ", description: " << jobInfo.logDetail[0].description << HCPENDLOG;

    HCP_Log(INFO, MODULE) << "Start JobService::ReportJobDetails" <<  HCPENDLOG;
    JobService::ReportJobDetails(ret,  jobInfo);
    HCP_Log(INFO, MODULE) << "Completed JobService::ReportJobDetails" <<  HCPENDLOG;
    if (ret.code != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Report job details to controller failed: " << ret.code <<  HCPENDLOG;
        return false;
    }
    return true;
}

bool HetroCommonService::ReadBackupCopyFromFile(std::string metaFileFullPath, HetroLastCopyDetails &backupCopy)
{
    std::ifstream readFd {};
    int retryCnt = 0;

    do {
        readFd.open(metaFileFullPath.c_str(), std::ios::in);
        if (readFd.is_open()) {
            break;
        }
        readFd.close();
        sleep(NUMBER1);
    } while (++retryCnt < NUMBER3);

    if (!readFd.is_open()) {
        char errmsg[NUMBER256];
        strerror_r(errno, errmsg, NUMBER256);
        HCP_Log(ERR, MODULE) << "Read " << WIPE_SENSITIVE(metaFileFullPath) << " failed, ERR: " << errmsg << HCPENDLOG;

        return false;
    }

    stringstream jsonFileContent {};
    jsonFileContent << readFd.rdbuf();
    readFd.close();
    HCP_Log(DEBUG, MODULE) << "BackupCopyMetaInfo read: " << WIPE_SENSITIVE(jsonFileContent.str()) <<  HCPENDLOG;

    if (!Module::JsonHelper::JsonStringToStruct(jsonFileContent.str(), backupCopy)) {
        ERRLOG("Read %s failed, ERR: Json covert failed", WIPE_SENSITIVE(metaFileFullPath).c_str());
        return false;
    }

    return true;
}

bool HetroCommonService::WriteBackupCopyToFile(std::string metaFileFullPath, HetroLastCopyDetails &backupCopy)
{
    std::ofstream writeFd {};
    int retryCnt = 0;

    do {
        writeFd.open(metaFileFullPath.c_str(), std::ios::out);
        if (writeFd.is_open()) {
            break;
        }
        writeFd.close();
        sleep(NUMBER1);
    } while (++retryCnt < NUMBER3);

    if (!writeFd.is_open()) {
        char errmsg[NUMBER256];
        strerror_r(errno, errmsg, NUMBER256);
        HCP_Log(ERR, MODULE) << "Write " << WIPE_SENSITIVE(metaFileFullPath)
            << " failed, ERR: " << errmsg << HCPENDLOG;

        return false;
    }

    std::string jsonFileContent {};
    if (!Module::JsonHelper::StructToJsonString(backupCopy, jsonFileContent)) {
        HCP_Log(ERR, MODULE) << "Write " << WIPE_SENSITIVE(metaFileFullPath)
        << " failed, ERR: Json covert failed" << HCPENDLOG;
        writeFd.close();
        return false;
    }

    HCP_Log(INFO, MODULE) << "write backup copy file, content = " << jsonFileContent << HCPENDLOG; // TO-DO remove
    writeFd << jsonFileContent;
    writeFd.close();
    return true;
}

bool HetroCommonService::CreateResource(const std::string key, const std::string &val,
    const std::string& mainJobId, const std::string& subJobId)
{
    int retryCnt = 0;
    ActionResult ret {};
    Resource res {};
    res.__set_sharedNum(1);
    res.__set_scope(ResourceScope::SINGLE_JOB);
    res.__set_scopeKey(key);
    res.__set_resourceKey(key);
    res.__set_resourceValue(val);

    do {
        if (retryCnt == MIDDLE_NORMAL_RETRY_TIMES) {
            HetroCommonService::KeepJobAlive(mainJobId, subJobId);
        }
        ShareResource::CreateResource(ret, res, mainJobId);
        if (ret.code != Module::FAILED) {
            DBGLOG("Create resource success for key: %s!", key.c_str());
            return true;
        }
        sleep(NORMAL_RETRY_INTERVAL);
        WARNLOG("CreateResource for key: %s failed, retryCnt: %d", key.c_str(), retryCnt);
    } while (++retryCnt <= NORMAL_RETRY_TIMES);
    ERRLOG("Create resource failed for key: %s, ret.code: %d", key.c_str(), ret.code);
    return false;
}

bool HetroCommonService::QueryResource(const std::string key, AppProtect::ResourceStatus &ret,
    const std::string& mainJobId, const std::string& subJobId)
{
    int retryCnt = 0;
    Resource res {};
    res.__set_sharedNum(1);
    res.__set_scope(ResourceScope::SINGLE_JOB);
    res.__set_scopeKey(key);
    res.__set_resourceKey(key);
    do {
        if (retryCnt % MIDDLE_EXECEPTION_RETRY_TIMES == 0) {
            HetroCommonService::KeepJobAlive(mainJobId, subJobId);
        }
        try {
            ShareResource::QueryResource(ret, res, mainJobId);
        } catch(AppProtectFrameworkException &e) {
            WARNLOG("QueryResource for key: %s failed, code: %d, retryCnt: %d", key.c_str(), e.code, retryCnt);
            if (e.code == E_QUERIED_RESOURCE_NOT_EXIST) {
                ERRLOG("Exit QueryResource for key: %s, resource not exist!", key.c_str());
                return false;
            }
            sleep(EXECEPTION_RETRY_INTERVAL);
            continue;
        }
        DBGLOG("Exit QueryResource for key: %s success", key.c_str());
        return true;
    } while (++retryCnt <= EXECEPTION_RETRY_TIMES);
    ERRLOG("Exit QueryResource for key: %s, after max retry & failed", key.c_str());
    return false;
}

bool HetroCommonService::UpdateResource(const std::string key, Resource &res,
    const std::string& mainJobId, const std::string& subJobId)
{
    int retryCnt = 0;
    ActionResult ret {};
    do {
        if (retryCnt == MIDDLE_NORMAL_RETRY_TIMES) {
            HetroCommonService::KeepJobAlive(mainJobId, subJobId);
        }
        ShareResource::UpdateResource(ret, res, mainJobId);
        if (ret.code != Module::FAILED) {
            DBGLOG("Exit UpdateResource for key: %s success", key.c_str());
            return true;
        }
        WARNLOG("UpdateResource for key: %s failed, retryCnt: %d", key.c_str(), retryCnt);
        sleep(NORMAL_RETRY_INTERVAL);
    } while (++retryCnt <= NORMAL_RETRY_TIMES);
    ERRLOG("Exit UpdateResource for key: %s, after max retry & failed", key.c_str());
    return false;
}

bool HetroCommonService::DeleteResource(
    const std::string key, const std::string& mainJobId, const std::string& subJobId)
{
    int retryCnt = 0;
    ActionResult ret {};
    Resource res {};
    res.__set_sharedNum(1);
    res.__set_scope(ResourceScope::SINGLE_JOB);
    res.__set_scopeKey(key);
    res.__set_resourceKey(key);
    do {
        if (retryCnt == MIDDLE_NORMAL_RETRY_TIMES) {
            HetroCommonService::KeepJobAlive(mainJobId, subJobId);
        }
        ShareResource::DeleteResource(ret, res, mainJobId);
        if (ret.code != Module::FAILED) {
            DBGLOG("Exit DeleteResource for key: %s success", key.c_str());
            return true;
        }
        WARNLOG("DeleteResource for key: %s failed, retryCnt: %d, code: %d", key.c_str(), retryCnt, ret.code);
        sleep(NORMAL_RETRY_INTERVAL);
    } while (++retryCnt <= NORMAL_RETRY_TIMES);
    ERRLOG("Exit DeleteResource for key: %s, after max retry & failed", key.c_str());
    return false;
}

bool HetroCommonService::LockResource(const std::string key, const uint32_t maxRetryCount, const std::string& mainJobId)
{
    uint32_t retryCnt = 0;
    ActionResult ret {};
    ret.code = 0;
    Resource res {};
    res.__set_sharedNum(1);
    res.__set_scope(ResourceScope::SINGLE_JOB);
    res.__set_scopeKey(key);
    res.__set_resourceKey(key);
    INFOLOG("Enter LockResource for key: %s", key.c_str());
    do {
        ShareResource::LockResource(ret, res, mainJobId);
        if (ret.code == MP_SUCCESS) {
            INFOLOG("Exit LockResource, success lock key: %s ", key.c_str());
            return true;
        }
        sleep(NUMBER3);
        WARNLOG("LockResource failed for key: %s, ret.code: %d, ret.bodyErr: %d, ret.message: %s, retryCnt: %d",
            key.c_str(), ret.code, ret.bodyErr, ret.message.c_str(), retryCnt);
    } while (++retryCnt < maxRetryCount);
    ERRLOG("Exit LockResource, failed lock key: %s after max retry, ret.code: %d, ret.message: %s, ret.bodyErr: %d",
        key.c_str(), ret.code, ret.message.c_str(), ret.bodyErr);
    return false;
}

bool HetroCommonService::UnlockResource(const std::string key, const uint32_t maxRetryCount,
    const std::string& mainJobId)
{
    uint32_t retryCnt = 0;
    ActionResult ret {};
    Resource res {};
    res.__set_sharedNum(1);
    res.__set_scope(ResourceScope::SINGLE_JOB);
    res.__set_scopeKey(key);
    res.__set_resourceKey(key);

    INFOLOG("Enter UnlockResource for key : %s", key.c_str());
    do {
        ShareResource::UnLockResource(ret, res, mainJobId);
        if (ret.code == MP_SUCCESS) {
            INFOLOG("Exit UnlockResource, success unlock key: %s ", key.c_str());
            return true;
        }
        sleep(NUMBER3);
        WARNLOG("UnlockResource failed for key: %s, ret.code: %d, ret.bodyErr: %d, retryCnt: %d",
            key.c_str(), ret.code, ret.bodyErr, retryCnt);
    } while (++retryCnt < maxRetryCount);
    ERRLOG("Exit UnlockResource, failed unlock key: %s after max retry, ret.code: %d, ret.bodyErr: %d",
        key.c_str(), ret.code, ret.bodyErr);
    return false;
}

bool HetroCommonService::CreateScanStatsResource(std::string key, HetroNativeScanStatistics &scanStats)
{
    key = key + SHARED_RSC_SCANNER_STATS_KEY;
    HCP_Log(DEBUG, MODULE) << "CreateScanStatsResource key is:" << key  << HCPENDLOG;

    std::string scanStatsString;
    if (!Module::JsonHelper::StructToJsonString(scanStats, scanStatsString)) {
        HCP_Log(ERR, MODULE) << "Create resource failed (convert to JSON failed) for key "
            << key << HCPENDLOG;
        return false;
    }
    if (!CreateResource(key, scanStatsString, m_jobId, m_subJobId)) {
        HCP_Log(ERR, MODULE) << "Create resource failed for key " << key;
        return false;
    }
    return true;
}

bool HetroCommonService::GetScanStatsResource(std::string key, HetroNativeScanStatistics &scanStats)
{
    key = key + SHARED_RSC_SCANNER_STATS_KEY;
    HCP_Log(DEBUG, MODULE) << "GetScanStatsResource key is:" << key  << HCPENDLOG;

    AppProtect::ResourceStatus res {};
    if (!QueryResource(key, res, m_jobId, m_subJobId)) {
        HCP_Log(ERR, MODULE) << "Get resource failed for key " << key << HCPENDLOG;
        return false;
    }
    if (!Module::JsonHelper::JsonStringToStruct(res.resource.resourceValue, scanStats)) {
        HCP_Log(ERR, MODULE) << "Get resource failed (convert to JSON failed) for key " << key << HCPENDLOG;
        return false;
    }
    return true;
}

bool HetroCommonService::UpdateScanStatsResource(std::string key, HetroNativeScanStatistics &scanStats)
{
    key = key + SHARED_RSC_SCANNER_STATS_KEY;
    HCP_Log(DEBUG, MODULE) << "UpdateScanStatsResource for key: " << key  << HCPENDLOG;

    Resource res {};
    res.__set_sharedNum(1);
    res.__set_scope(ResourceScope::SINGLE_JOB);
    res.__set_scopeKey(key);
    res.__set_resourceKey(key);

    std::string scanStatsString;
    if (!Module::JsonHelper::StructToJsonString(scanStats, scanStatsString)) {
        HCP_Log(ERR, MODULE) << "Update resource failed (convert to JSON failed) for key: " << key << HCPENDLOG;
        return false;
    }

    res.__set_resourceValue(scanStatsString);
    if (!UpdateResource(key, res, m_jobId, m_subJobId)) {
        HCP_Log(ERR, MODULE) << "UpdateScanStatsResource failed for key: " << key << HCPENDLOG;
        return false;
    }

    return true;
}

bool HetroCommonService::DeleteScanStatsResource(std::string key)
{
    key = key + SHARED_RSC_SCANNER_STATS_KEY;
    HCP_Log(DEBUG, MODULE) << "DeleteScanStatsResource key is: " << key  << HCPENDLOG;

    if (!DeleteResource(key, m_jobId, m_subJobId)) {
        HCP_Log(ERR, MODULE) << "DeleteScanStatsResource failed for key: " << key;
        return false;
    }
    return true;
}

bool HetroCommonService::LockBackupStatsResource(std::string mainJobId)
{
    std::string key = mainJobId + SHARED_RSC_BACKUP_STATS_KEY;
    return LockResource(key, NUMBER0, mainJobId);
}

bool HetroCommonService::UnlockBackupStatsResource(std::string mainJobId)
{
    std::string key = mainJobId + SHARED_RSC_BACKUP_STATS_KEY;
    return UnlockResource(key, NUMBER0, mainJobId);
}

bool HetroCommonService::CreateBackupStatsResource(std::string key, HetroNativeBackupStats &backupStats)
{
    key = key + SHARED_RSC_BACKUP_STATS_KEY;
    HCP_Log(DEBUG, MODULE) << "CreateBackupStatsResource  key is:" << key  << HCPENDLOG;

    std::string backupStatsString;
    if (!Module::JsonHelper::StructToJsonString(backupStats, backupStatsString)) {
        HCP_Log(ERR, MODULE) << "Create resource failed (convert to JSON failed) for key " << key << HCPENDLOG;
        return false;
    }
    if (!CreateResource(key, backupStatsString, m_jobId, m_subJobId)) {
        HCP_Log(ERR, MODULE) << "Create resource failed for key " << key << HCPENDLOG;
        return false;
    }
    return true;
}

bool HetroCommonService::GetBackupStatsResource(std::string key, HetroNativeBackupStats &backupStats)
{
    key = key + SHARED_RSC_BACKUP_STATS_KEY;
    HCP_Log(DEBUG, MODULE) << "GetBackupStatsResource  key is:" << key  << HCPENDLOG;

    AppProtect::ResourceStatus res {};
    if (!QueryResource(key, res, m_jobId, m_subJobId)) {
        HCP_Log(ERR, MODULE) << "Get resource failed for key: " << key << HCPENDLOG;
        return false;
    }
    if (!Module::JsonHelper::JsonStringToStruct(res.resource.resourceValue, backupStats)) {
        HCP_Log(ERR, MODULE) << "Get resource failed (convert to JSON failed) for key " << key << HCPENDLOG;
        return false;
    }

    return true;
}

bool HetroCommonService::UpdateBackupStatsResource(std::string key, HetroNativeBackupStats &backupStats)
{
    key = key + SHARED_RSC_BACKUP_STATS_KEY;
    HCP_Log(DEBUG, MODULE) << "UpdateBackupStatsResource for key: " << key  << HCPENDLOG;

    Resource res {};
    res.__set_sharedNum(1);
    res.__set_scope(ResourceScope::SINGLE_JOB);
    res.__set_scopeKey(key);
    res.__set_resourceKey(key);

    std::string backupStatsString;
    if (!Module::JsonHelper::StructToJsonString(backupStats, backupStatsString)) {
        HCP_Log(ERR, MODULE) << "Update resource failed (convert to JSON failed) for key: " << key << HCPENDLOG;
        return false;
    }

    res.__set_resourceValue(backupStatsString);
    if (!UpdateResource(key, res, m_jobId, m_subJobId)) {
        return false;
    }

    return true;
}

bool HetroCommonService::DeleteBackupStatsResource(std::string key)
{
    key = key + SHARED_RSC_BACKUP_STATS_KEY;
    HCP_Log(DEBUG, MODULE) << "DeleteBackupStatsResource key is:" << key << HCPENDLOG;

    if (!DeleteResource(key, m_jobId, m_subJobId)) {
        HCP_Log(ERR, MODULE) << "Delete resource failed for key: " << key << HCPENDLOG;
        return false;
    }
    return true;
}

bool HetroCommonService::LockGeneralResource(std::string mainJobId)
{
    std::string key = mainJobId + SHARED_RSC_GENEAL_INFO_KEY;
    return LockResource(key, NUMBER100, mainJobId);
}

bool HetroCommonService::UnlockGeneralResource(std::string mainJobId)
{
    std::string key = mainJobId + SHARED_RSC_GENEAL_INFO_KEY;
    return UnlockResource(key, NUMBER10, mainJobId);
}

bool HetroCommonService::CreateGeneralResource(std::string key, HetroNativeGeneral &generalInfo)
{
    if (GetGeneralResource(key, generalInfo)) {
        HCP_Log(WARN, MODULE) << "Resource for key " << key << " already exist! "
                        << "generalInfo.m_jobStartTime = " << generalInfo.m_jobStartTime
                        << ", generalInfo.m_protocolVersion = " << generalInfo.m_protocolVersion
                        << ", generalInfo.m_remoteNasShareSnapshotName = " << generalInfo.m_remoteNasShareSnapshotName
                        << ", generalInfo.m_remoteNasShareSnapshotPath = " << generalInfo.m_remoteNasShareSnapshotPath
                        << ", generalInfo.m_remoteNasShareSnapshotTime = " << generalInfo.m_remoteNasShareSnapshotTime
                        << ", generalInfo.m_backupCopyPhaseStartTime = " << generalInfo.m_backupCopyPhaseStartTime
                        << ", generalInfo.m_backupDelPhaseStartTime = " << generalInfo.m_backupDelPhaseStartTime
                        << HCPENDLOG;
        return true;
    }

    key = key + SHARED_RSC_GENEAL_INFO_KEY;

    std::string generalInfoString;
    if (!Module::JsonHelper::StructToJsonString(generalInfo, generalInfoString)) {
        HCP_Log(ERR, MODULE) << "Create resource failed (convert to JSON failed) for key " << key << HCPENDLOG;
        return false;
    }
    if (!CreateResource(key, generalInfoString, m_jobId, m_subJobId)) {
        HCP_Log(ERR, MODULE) << "Create resource failed for key:" << key << HCPENDLOG;
        return false;
    }
    HCP_Log(DEBUG, MODULE) << "CreateGeneralResource key: " << key << ", value: " << generalInfoString << HCPENDLOG;
    return true;
}

bool HetroCommonService::GetGeneralResource(std::string key, HetroNativeGeneral &generalInfo)
{
    key = key + SHARED_RSC_GENEAL_INFO_KEY;
    HCP_Log(DEBUG, MODULE) << "GetGeneralResource key is:" << key << HCPENDLOG;

    AppProtect::ResourceStatus res {};
    if (!QueryResource(key, res, m_jobId, m_subJobId)) {
        HCP_Log(ERR, MODULE) << "Get resource failed for key " << key << HCPENDLOG;
        return false;
    }
    if (!Module::JsonHelper::JsonStringToStruct(res.resource.resourceValue, generalInfo)) {
        HCP_Log(ERR, MODULE) << "Get resource failed (convert to JSON failed) for key " << key << HCPENDLOG;
        return false;
    }

    HCP_Log(DEBUG, MODULE) << "GetGeneralResource key: " << key << ", val: "<< res.resource.resourceValue << HCPENDLOG;
    return true;
}

bool HetroCommonService::UpdateGeneralResource(std::string key, HetroNativeGeneral &generalInfo)
{
    key = key + SHARED_RSC_GENEAL_INFO_KEY;
    HCP_Log(DEBUG, MODULE) << "UpdateGeneralResource key is:" << key << HCPENDLOG;

    Resource res {};
    res.__set_sharedNum(1);
    res.__set_scope(ResourceScope::SINGLE_JOB);
    res.__set_scopeKey(key);
    res.__set_resourceKey(key);

    std::string generalInfoString;
    if (!Module::JsonHelper::StructToJsonString(generalInfo, generalInfoString)) {
        HCP_Log(ERR, MODULE) << "Update resource failed (convert to JSON failed) for key " << key << HCPENDLOG;
        return false;
    }

    res.__set_resourceValue(generalInfoString);
    if (!UpdateResource(key, res, m_jobId, m_subJobId)) {
        HCP_Log(ERR, MODULE) << "UpdateGeneralResource failed for key: " << key << HCPENDLOG;
        return false;
    }

    HCP_Log(DEBUG, MODULE) << "UpdateGeneralResource key: " << key << ", value: " << generalInfoString << HCPENDLOG;
    return true;
}

bool HetroCommonService::DeleteGeneralResource(std::string key)
{
    key = key + SHARED_RSC_GENEAL_INFO_KEY;
    HCP_Log(DEBUG, MODULE) << "DeleteGeneralResource key is:" << key << HCPENDLOG;

    if (!DeleteResource(key, m_jobId, m_subJobId)) {
        HCP_Log(ERR, MODULE) << "Delete resource failed for key " << key << HCPENDLOG;
        return false;
    }
    return true;
}

uint64_t HetroCommonService::GetCurrentTimeFromRemoteServer(std::string path)
{
    std::string errMsg = "";
    return GetCurrentTimeFromRemoteServerWithMsg(path, errMsg);
}

uint64_t HetroCommonService::GetCurrentTimeFromRemoteServerWithMsg(std::string path, std::string &errMsg)
{
    std::string tempFileName = CreateUniqueTempFileName(path);
    std::ofstream tempFile {};
    int retryCnt = 0;

    // 1. Create a temp file (dpa_current-time)
    do {
        tempFile.open(tempFileName.c_str(), std::ios::out | std::ios::binary);
        if (tempFile.is_open()) {
            break;
        }
        tempFile.close();
        sleep(NUMBER1);
    } while (++retryCnt < NUMBER3);

    if (!tempFile.is_open()) {
        errMsg = hcp_strerror(errno);
        HCP_Log(ERR, MODULE) << "Failed to open file (after 3 retries): " << WIPE_SENSITIVE(tempFileName)
            << ", ERR: " << errMsg << HCPENDLOG;
        return 0;
    }
    tempFile.close();

    // 2. Get the ctime of the temp file
    struct stat info {};
    if (stat(tempFileName.c_str(), &info) != 0) {
        errMsg = hcp_strerror(errno);
        HCP_Log(ERR, MODULE) << "Stat failed for the temp file in remote NAS: "
            << WIPE_SENSITIVE(tempFileName) << HCPENDLOG;
        return 0;
    }
    time_t createTime = info.st_ctime;

    // 3. Delete the file
    try {
        if (boost::filesystem::remove(tempFileName) == false) {
            errMsg = hcp_strerror(errno);
            HCP_Log(ERR, MODULE) << "Failure when removing file: " << WIPE_SENSITIVE(tempFileName) << HCPENDLOG;
            return 0;
        }
    } catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "remove() exeption: " << WIPE_SENSITIVE(e.code().message())
            << ", path: " << WIPE_SENSITIVE(tempFileName);
        errMsg = hcp_strerror(EINVAL);
        return 0;
    }

    // 4. return the ctime of the file
    uint64_t currTimeOfRemoteServer = static_cast<uint64_t>(createTime);
    HCP_Log(DEBUG, MODULE) << "Current Time Of Remote Server: " << currTimeOfRemoteServer;
    return currTimeOfRemoteServer;
}

std::string HetroCommonService::CreateUniqueTempFileName(std::string path)
{
    string tempFileName {};
    bool isFileExist = false;
    boost::system::error_code ec;

    do {
        tempFileName = path + "/" + "dpa_" + GetUniqueId();

        try {
            isFileExist = boost::filesystem::exists(tempFileName, ec);
            HCP_Log(DEBUG, MODULE) << "check file exists End, file name=" << tempFileName << HCPENDLOG;

            if (ec.value() != boost::system::errc::success) {
                HCP_Log(DEBUG, MODULE) << "boost::filesystem::exists() reported failure as: " << ec.message()
                    << ". filename: " << tempFileName << HCPENDLOG;
            }
        } catch (const boost::filesystem::filesystem_error &e) {
            HCP_Log(ERR, MODULE) << "boost::filesystem::exists() exeption: " << e.code().message()
                << " path " << tempFileName << HCPENDLOG;
        }
    } while (isFileExist);

    return tempFileName;
}

bool HetroCommonService::WriteBackupStatsToFile(std::string filePath, HetroNativeBackupStats &backupStatistics)
{
    std::string jsonFileContent {};
    if (!Module::JsonHelper::StructToJsonString(backupStatistics, jsonFileContent)) {
        HCP_Log(ERR, MODULE) << "Write " << WIPE_SENSITIVE(filePath) << " failed, ERR: Json covert failed" << HCPENDLOG;
        return false;
    }
    std::vector<std::string> fileContent {};
    fileContent.push_back(jsonFileContent);
    return WriteFile(filePath, fileContent);
}

bool HetroCommonService::ReadBackupStatsFromFile(std::string filePath, HetroNativeBackupStats &backupStatistics)
{
    std::vector<std::string> fileContent {};
    if (!ReadFile(filePath, fileContent)) {
        return false;
    }
    if (fileContent.empty()) {
        WARNLOG("SubJobStatsFile content is empty");
        return true;
    }
    std::string jsonFileContent = fileContent.front();
    if (jsonFileContent.empty()) {
        WARNLOG("SubJobStatsFile content is empty");
        return true;
    }
    if (!Module::JsonHelper::JsonStringToStruct(jsonFileContent, backupStatistics)) {
        ERRLOG("Read %s failed, ERR: json convert failed", filePath.c_str());
        return false;
    }
    DBGLOG("SubJobStatsFile content: %s", jsonFileContent.c_str());
    return true;
}

void HetroCommonService::CleanKrbFilesForCifs(std::string jobId)
{
    HCP_Log(DEBUG, MODULE) << "Begin to clean kerberos file for job: " << jobId << HCPENDLOG;
    std::string agentHomePath = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    std::string krb5ConfFile = agentHomePath + KRB5CONFIGPREFIX + jobId + KRB5CONFIGPOSTFIX;
    std::string krb5CcacheFile = agentHomePath + KRB5CCNAMEPREFIX + jobId;
    std::string krb5KeytabFile = agentHomePath + KRB5KEYTABPREFIX + jobId + KRB5KEYTABPOSTFIX;
    struct stat info {};
    if (stat(krb5ConfFile.c_str(), &info) != 0) {
        HCP_Log(ERR, MODULE) << "no krb5.conf file for job: " << jobId << HCPENDLOG;
    }
    remove(krb5ConfFile.c_str());

    if (stat(krb5CcacheFile.c_str(), &info) != 0) {
        HCP_Log(ERR, MODULE) << "no krb5 ccache file for job: " << jobId << HCPENDLOG;
    }
    remove(krb5CcacheFile.c_str());

    if (stat(krb5KeytabFile.c_str(), &info) == 0) {
        remove(krb5KeytabFile.c_str());
    }
}

bool HetroCommonService::CreateSharedResources(std::string jobId, HetroNativeGeneral &generalInfo,
    HetroNativeScanStatistics &scanStats, HetroNativeBackupStats &backupStats)
{
    if (!CreateGeneralResource(jobId, generalInfo)) {
        HCP_Log(ERR, MODULE) << "CreateGeneralResource failed" << HCPENDLOG;
        return false;
    }
    if (!CreateScanStatsResource(jobId, scanStats)) {
        HCP_Log(ERR, MODULE) << "CreateScanStatsResource failed" << HCPENDLOG;
        DeleteGeneralResource(jobId);
        return false;
    }
    if (!CreateBackupStatsResource(jobId, backupStats)) {
        HCP_Log(ERR, MODULE) << "CreateBackupStatsResource failed" << HCPENDLOG;
        DeleteGeneralResource(jobId);
        DeleteScanStatsResource(jobId);
        return false;
    }
    return true;
}

bool HetroCommonService::GetSharedResources(std::string jobId, HetroNativeGeneral &generalInfo,
    HetroNativeScanStatistics &scanStats, HetroNativeBackupStats &backupStats)
{
    if (!GetGeneralResource(jobId, generalInfo)) {
        HCP_Log(ERR, MODULE) << "GetGeneralResource failed" << HCPENDLOG;
        return false;
    }
    if (!GetScanStatsResource(jobId, scanStats)) {
        HCP_Log(ERR, MODULE) << "GetScanStatsResource failed" << HCPENDLOG;
        return false;
    }
    if (!GetBackupStatsResource(jobId, backupStats)) {
        HCP_Log(ERR, MODULE) << "GetBackupStatsResource failed" << HCPENDLOG;
        return false;
    }
    return true;
}

bool HetroCommonService::DeleteSharedResources(std::string jobId)
{
    if (!DeleteGeneralResource(jobId)) {
        HCP_Log(ERR, MODULE) << "DeleteGeneralResource failed" << HCPENDLOG;
    }
    if (!DeleteScanStatsResource(jobId)) {
        HCP_Log(ERR, MODULE) << "DeleteScanStatsResource failed" << HCPENDLOG;
    }
    if (!DeleteBackupStatsResource(jobId)) {
        HCP_Log(ERR, MODULE) << "DeleteBackupStatsResource failed" << HCPENDLOG;
    }
    return true;
}

void HetroCommonService::FillBackupSmbConfigAuthInfo(BackupParams &backupParams, std::string &jobId,
    StorageRepository &m_dataFs, ProtectedNasShare &m_nasShare, BackupType jobType)
{
    std::shared_ptr<LibsmbBackupAdvanceParams> srcAdvParams =
        dynamic_pointer_cast<LibsmbBackupAdvanceParams>(backupParams.srcAdvParams);
    std::shared_ptr<LibsmbBackupAdvanceParams> dstAdvParams =
        dynamic_pointer_cast<LibsmbBackupAdvanceParams>(backupParams.dstAdvParams);
    std::string agentHomePath = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    std::string krb5ConfFile = agentHomePath + KRB5CONFIGPREFIX + jobId + KRB5CONFIGPOSTFIX;
    srcAdvParams->krb5ConfigFile = agentHomePath + KRB5CONFIGPREFIX + jobId + KRB5CONFIGPOSTFIX;
    srcAdvParams->krb5CcacheFile = agentHomePath + KRB5CCNAMEPREFIX + jobId;

    dstAdvParams->krb5ConfigFile = agentHomePath + KRB5CONFIGPREFIX + jobId + KRB5CONFIGPOSTFIX;
    dstAdvParams->krb5CcacheFile = agentHomePath + KRB5CCNAMEPREFIX + jobId;

    if (jobType == BackupType::RESTORE) {
        srcAdvParams->user = m_dataFs.auth.authkey;
        srcAdvParams->password = m_dataFs.auth.authPwd;

        dstAdvParams->user = m_nasShare.auth.authkey;
        dstAdvParams->password = m_nasShare.auth.authPwd;
        dstAdvParams->domain = m_nasShare.nasShareExt.m_domainName;
    } else {
        srcAdvParams->user = m_nasShare.auth.authkey;
        srcAdvParams->password = m_nasShare.auth.authPwd;
        srcAdvParams->domain = m_nasShare.nasShareExt.m_domainName;

        dstAdvParams->user = m_dataFs.auth.authkey;
        dstAdvParams->password = m_dataFs.auth.authPwd;
    }
}

bool HetroCommonService::UpdateScannerStatistics(HetroNativeScanStatistics &scanStatistics,
    HetroNativeScanStatistics &scanStats, bool isScannerRestarted, std::string &jobId)
{
    ScanStatistics stats = m_scanner->GetStatistics();

    scanStatistics.m_totDirs = stats.mTotDirs;
    scanStatistics.m_totFiles = stats.mTotFiles;
    scanStatistics.m_totFailedDirs = stats.mTotFailedDirs;
    scanStatistics.m_totFailedFiles = stats.mTotFailedFiles;
    scanStatistics.m_totDirsToBackup = stats.mTotDirsToBackup;
    scanStatistics.m_totFilesToBackup = stats.mTotFilesToBackup;
    scanStatistics.m_totDirsDeleted = stats.mTotDirsDeleted;
    scanStatistics.m_totFilesDeleted = stats.mTotFilesDeleted;
    scanStatistics.m_totalControlFiles = stats.mTotalControlFiles;
    scanStatistics.m_totalSize = stats.mTotalSize;
    scanStatistics.m_totalSizeToBackup = stats.mTotalSizeToBackup;

    HetroNativeScanStatistics localscanStats {};
    localscanStats.m_scanStarted = scanStats.m_scanStarted;
    localscanStats.m_scanStartTime = scanStatistics.m_scanStartTime;
    localscanStats.m_remoteNasScanEndTime = scanStatistics.m_remoteNasScanEndTime;
    localscanStats.m_scanEndTime = scanStatistics.m_scanEndTime;

    if (isScannerRestarted) {
        localscanStats.m_totDirs = scanStats.m_totDirs + scanStatistics.m_totDirs;
        localscanStats.m_totDirsToBackup = scanStats.m_totDirsToBackup + scanStatistics.m_totDirsToBackup;
        localscanStats.m_totDirsDeleted = scanStats.m_totDirsDeleted + scanStatistics.m_totDirsDeleted;
        localscanStats.m_totFailedDirs = scanStats.m_totFailedDirs + scanStatistics.m_totFailedDirs;
        localscanStats.m_totFiles = scanStats.m_totFiles + scanStatistics.m_totFiles;
        localscanStats.m_totFilesToBackup = scanStats.m_totFilesToBackup + scanStatistics.m_totFilesToBackup;
        localscanStats.m_totFilesDeleted = scanStats.m_totFilesDeleted + scanStatistics.m_totFilesDeleted;
        localscanStats.m_totFailedFiles = scanStats.m_totFailedFiles + scanStatistics.m_totFailedFiles;
        localscanStats.m_totalSize = scanStats.m_totalSize + scanStatistics.m_totalSize;
        localscanStats.m_totalSizeToBackup = scanStats.m_totalSizeToBackup + scanStatistics.m_totalSizeToBackup;
        localscanStats.m_totalControlFiles = scanStats.m_totalControlFiles + scanStatistics.m_totalControlFiles;
    } else {
        localscanStats.m_totDirs = scanStatistics.m_totDirs;
        localscanStats.m_totDirsToBackup = scanStatistics.m_totDirsToBackup;
        localscanStats.m_totDirsDeleted = scanStatistics.m_totDirsDeleted;
        localscanStats.m_totFailedDirs = scanStatistics.m_totFailedDirs;
        localscanStats.m_totFiles = scanStatistics.m_totFiles;
        localscanStats.m_totFilesToBackup = scanStatistics.m_totFilesToBackup;
        localscanStats.m_totFilesDeleted = scanStatistics.m_totFilesDeleted;
        localscanStats.m_totFailedFiles = scanStatistics.m_totFailedFiles;
        localscanStats.m_totalSize = scanStatistics.m_totalSize;
        localscanStats.m_totalSizeToBackup = scanStatistics.m_totalSizeToBackup;
        localscanStats.m_totalControlFiles = scanStatistics.m_totalControlFiles;
    }

    PrintScannerStatistics(localscanStats);

    if (!UpdateScanStatsResource(jobId, localscanStats)) {
        return false;
    }
    return true;
}

ScanStatistics HetroCommonService::AddScanStatistics(const ScanStatistics& stats_A, const ScanStatistics& stats_B)
{
    ScanStatistics scanStatistics;
    scanStatistics.mTotDirs = stats_A.mTotDirs + stats_B.mTotDirs;
    scanStatistics.mTotFiles = stats_A.mTotFiles + stats_B.mTotFiles;
    scanStatistics.mTotalSize = stats_A.mTotalSize + stats_B.mTotalSize;
    scanStatistics.mTotDirsToBackup = stats_A.mTotDirsToBackup + stats_B.mTotDirsToBackup;
    scanStatistics.mTotFilesToBackup = stats_A.mTotFilesToBackup + stats_B.mTotFilesToBackup;
    scanStatistics.mTotFilesDeleted = stats_A.mTotFilesDeleted + stats_B.mTotFilesDeleted;
    scanStatistics.mTotDirsDeleted = stats_A.mTotDirsDeleted + stats_B.mTotDirsDeleted;
    scanStatistics.mTotalSizeToBackup = stats_A.mTotalSizeToBackup + stats_B.mTotalSizeToBackup;
    scanStatistics.mTotalControlFiles = stats_A.mTotalControlFiles + stats_B.mTotalControlFiles;
    scanStatistics.mTotFailedDirs = stats_A.mTotFailedDirs + stats_B.mTotFailedDirs;
    scanStatistics.mTotFailedFiles = stats_A.mTotFailedFiles + stats_B.mTotFailedFiles;
    return scanStatistics;
}

void HetroCommonService::PrintScannerStatistics(HetroNativeScanStatistics &nativeScanStats)
{
    std::string scanStartTimeStr = FormatTimeToStr(nativeScanStats.m_scanStartTime);
    std::string remoteNasScanEndTimeStr = FormatTimeToStr(nativeScanStats.m_remoteNasScanEndTime);
    std::string scanEndTimeStr = FormatTimeToStr(nativeScanStats.m_scanEndTime);

    int32_t rmtNasScanDur = double(nativeScanStats.m_remoteNasScanEndTime - nativeScanStats.m_scanStartTime);
    int32_t remoteNasScanSpeed = (rmtNasScanDur == 0) ? nativeScanStats.m_totFiles :
        (nativeScanStats.m_totFiles / rmtNasScanDur);

    int32_t scanDuration = double(nativeScanStats.m_scanEndTime - nativeScanStats.m_scanStartTime);
    int32_t scanSpeed = (scanDuration == 0) ? nativeScanStats.m_totFiles : (nativeScanStats.m_totFiles / scanDuration);

    INFOLOG("Scan status               : %d", nativeScanStats.m_scanStatus);
    INFOLOG("Scan start time           : %s", scanStartTimeStr.c_str());
    INFOLOG("# of dirs scanned         : %llu", nativeScanStats.m_totDirs);
    INFOLOG("# of files scanned        : %llu", nativeScanStats.m_totFiles);
    INFOLOG("# of dirs Failed to scan  : %llu", nativeScanStats.m_totFailedDirs);
    INFOLOG("# of files Failed to scan : %llu", nativeScanStats.m_totFailedFiles);
    INFOLOG("Total Size                : %s", FormatCapacity(nativeScanStats.m_totalSize).c_str());
    INFOLOG("# of dirs to bkup         : %llu", nativeScanStats.m_totDirsToBackup);
    INFOLOG("# of files to bkup        : %llu", nativeScanStats.m_totFilesToBackup);
    INFOLOG("# of dirs to del          : %llu", nativeScanStats.m_totDirsDeleted);
    INFOLOG("# of files to del         : %llu", nativeScanStats.m_totFilesDeleted);
    INFOLOG("Size to bkup              : %s", FormatCapacity(nativeScanStats.m_totalSizeToBackup).c_str());
    INFOLOG("# control files           : %llu", nativeScanStats.m_totalControlFiles);

    if (nativeScanStats.m_scanStatus == (int)SCANNER_STATUS::SCAN_COMPLETED) {
        INFOLOG("Remote NAS Scan end time  : %s", remoteNasScanEndTimeStr.c_str());
        INFOLOG("Overall Scan end time     : %s", scanEndTimeStr.c_str());
        INFOLOG("Remote NAS Scan duration  : %d sec", rmtNasScanDur);
        INFOLOG("Remote NAS Scan speed     : %d files/sec", remoteNasScanSpeed);
        INFOLOG("Scan duration             : %d sec", scanDuration);
        INFOLOG("Scan speed                : %d files/sec", scanSpeed);
    }
}

void HetroCommonService::PrintBackupStatistics(BackupStats &backupStatistics,
    std::string &jobId, BackupPhaseStatus m_backupStatus, std::string &statPrintSubJobId)
{
    std::string backupStartTimeStr = FormatTimeToStr(backupStatistics.startTime);
    INFOLOG("Backup Curr Job %s, subjob Cnt: %llu", jobId.c_str(), g_nodeLevelTaskInfo.GetRunningSubTasksCount());
    INFOLOG("Backup Total Job Executed Cnt : %llu", g_nodeLevelTaskInfo.GetSubTasksCount());
    INFOLOG("Backup Total Job Submitted Cnt: %llu", g_nodeLevelTaskInfo.GetSubTasksSubmitCount());
    INFOLOG("Backup status                 : %d, %s", static_cast<int>(m_backupStatus), statPrintSubJobId.c_str());
    INFOLOG("Backup start time             : %s", backupStartTimeStr.c_str());
    INFOLOG("Backup Speed                  : %s", FormatSpeed(backupStatistics.backupspeed).c_str());
    INFOLOG("# noOfDirToBackup             : %llu", backupStatistics.noOfDirToBackup);
    INFOLOG("# noOfFilesToBackup           : %llu", backupStatistics.noOfFilesToBackup);
    INFOLOG("# noOfBytesToBackup           : %s", FormatCapacity(backupStatistics.noOfBytesToBackup).c_str());
    INFOLOG("# noOfDirToDelete             : %llu", backupStatistics.noOfDirToDelete);
    INFOLOG("# noOfFilesToDelete           : %llu", backupStatistics.noOfFilesToDelete);
    INFOLOG("# noOfDirCopied               : %llu", backupStatistics.noOfDirCopied);
    INFOLOG("# noOfFilesCopied             : %llu", backupStatistics.noOfFilesCopied);
    INFOLOG("# noOfBytesCopied             : %s", FormatCapacity(backupStatistics.noOfBytesCopied).c_str());
    INFOLOG("# noOfDirDeleted              : %llu", backupStatistics.noOfDirDeleted);
    INFOLOG("# noOfFilesDeleted            : %llu", backupStatistics.noOfFilesDeleted);
    INFOLOG("# noOfDirFailed               : %llu", backupStatistics.noOfDirFailed);
    INFOLOG("# noOfFilesFailed             : %llu", backupStatistics.noOfFilesFailed);
    INFOLOG("# noOfSrcRetryCount           : %llu", backupStatistics.noOfSrcRetryCount);
    INFOLOG("# noOfDstRetryCount           : %llu", backupStatistics.noOfDstRetryCount);
    INFOLOG("# Backup noOfFailureRecordsWritten: \t%llu", backupStatistics.noOfFailureRecordsWritten);
}

// create a check subjob task, check whether have failed files or dirs
bool HetroCommonService::CreateCheckSubTask()
{
    std::string backupSubJobStr {};
    HetroBackupSubJob backupSubJob {};
    SubJob subJob {};
    backupSubJob.m_SubTaskType = SUBJOB_TYPE_CHECK_SUBJOB_PHASE;

    if (!Module::JsonHelper::StructToJsonString(backupSubJob, backupSubJobStr)) {
        HCP_Log(ERR, MODULE) << "Exit CreateBackupJobTaskToCreateFurtherSubTasks failed" << HCPENDLOG;
        return false;
    }
    subJob.__set_jobId(m_jobId);
    subJob.__set_jobType(SubJobType::BUSINESS_SUB_JOB);
    subJob.__set_policy(ExecutePolicy::ANY_NODE);
    subJob.__set_jobInfo(backupSubJobStr);
    // 设置为忽略失败，如果存在失败文件或目录，此任务失败，UBC置为部分成功
    subJob.__set_ignoreFailed(true);

    std::string createSubJobName = SUBJOB_TYPE_CHECK_SUBJOB_JOBNAME + m_jobId;
    uint32_t subTaskPrio = SUBJOB_TYPE_CHECK_SUBJOB_PHASE_PRIO;
    subJob.__set_jobPriority(subTaskPrio);
    subJob.__set_jobName(createSubJobName);

    do {
        if (IsAbortJob()) {
            WARNLOG("Exit received Abort for taskid: %s, subtaskid: %s", m_jobId.c_str(), m_subJobId.c_str());
            break;
        }
        int ret = CreateSubTask(subJob);
        if (ret == Module::SUCCESS) {
            break;
        } else if (ret == Module::RETRY) {
            HCP_Log(WARN, MODULE) << "Create subtask failed with retriable error"<< HCPENDLOG;
            SleepForCreateSubTaskError();
            continue;
        } else {
            ERRLOG("Exit CreateBackupJobTaskToCreateFurtherSubTasks, CreateSubtask failed");
            return false;
        }
    } while (true);
    return true;
}

bool HetroCommonService::UpdateBackupSubTaskStatistics(BackupStats &backupStatistics,
    uint32_t &subTaskType, std::string &metaFsPath,
    std::shared_ptr<SubJob> &subJobInfo)
{
    HetroNativeBackupStats subBackupJobStatistics {};
    std::string filePath {};
    subBackupJobStatistics.m_noOfDirToBackup    = backupStatistics.noOfDirToBackup;
    subBackupJobStatistics.m_noOfFilesToBackup  = backupStatistics.noOfFilesToBackup;
    subBackupJobStatistics.m_noOfBytesToBackup  = backupStatistics.noOfBytesToBackup;
    subBackupJobStatistics.m_noOfDirToDelete    = backupStatistics.noOfDirToDelete;
    subBackupJobStatistics.m_noOfFilesToDelete  = backupStatistics.noOfFilesToDelete;
    subBackupJobStatistics.m_noOfDirCopied      = backupStatistics.noOfDirCopied;
    subBackupJobStatistics.m_noOfFilesCopied    = backupStatistics.noOfFilesCopied;
    subBackupJobStatistics.m_noOfBytesCopied    = backupStatistics.noOfBytesCopied;
    subBackupJobStatistics.m_noOfDirDeleted     = backupStatistics.noOfDirDeleted;
    subBackupJobStatistics.m_noOfFilesDeleted   = backupStatistics.noOfFilesDeleted;
    subBackupJobStatistics.m_noOfDirFailed      = backupStatistics.noOfDirFailed;
    subBackupJobStatistics.m_noOfFilesFailed    = backupStatistics.noOfFilesFailed;
    subBackupJobStatistics.m_noOfSrcRetryCount  = backupStatistics.noOfSrcRetryCount;
    subBackupJobStatistics.m_noOfDstRetryCount  = backupStatistics.noOfDstRetryCount;
    subBackupJobStatistics.m_noOfFailureRecordsWritten  = backupStatistics.noOfFailureRecordsWritten;
    subBackupJobStatistics.m_noOfFilesWriteSkip = backupStatistics.noOfFilesWriteSkip;
    subBackupJobStatistics.m_skipFileCnt = backupStatistics.skipFileCnt;

    // Ignore updating the shared resource for DirMtime phase
    if (subTaskType != SUBJOB_TYPE_DATACOPY_DIRMTIME_PHASE) {
        filePath = metaFsPath + "/statistics_" + m_jobId + "/backup-stats-sub-" + subJobInfo->subJobId + ".json";
    } else {
        filePath = metaFsPath + "/statistics_" + m_jobId + "/backup-stats-sub-dirmtime-" +
            subJobInfo->subJobId + ".json";
    }
    WriteBackupStatsToFile(filePath, subBackupJobStatistics);
    return true;
}

void HetroCommonService::PrintSubJobInfo(std::shared_ptr<SubJob> &m_subJobInfo)
{
    HCP_Log(DEBUG, MODULE) << "m_subJobInfo->jobId       : " << m_subJobInfo->jobId << HCPENDLOG;
    HCP_Log(DEBUG, MODULE) << "m_subJobInfo->subJobId    : " << m_subJobInfo->subJobId << HCPENDLOG;
    HCP_Log(DEBUG, MODULE) << "m_subJobInfo->jobType     : " << m_subJobInfo->jobType << HCPENDLOG;
    HCP_Log(DEBUG, MODULE) << "m_subJobInfo->jobName     : " << m_subJobInfo->jobName << HCPENDLOG;
    HCP_Log(DEBUG, MODULE) << "m_subJobInfo->jobPriority : " << m_subJobInfo->jobPriority << HCPENDLOG;
    HCP_Log(DEBUG, MODULE) << "m_subJobInfo->policy      : " << m_subJobInfo->policy << HCPENDLOG;
    HCP_Log(DEBUG, MODULE) << "m_subJobInfo->jobInfo     : " << m_subJobInfo->jobInfo << HCPENDLOG;
    return;
}

bool HetroCommonService::IsUpdateBackupStartTimeRequired(HetroBackupSubJob& backupSubJob,
    HetroNativeGeneral &generalInfo)
{
    if (backupSubJob.m_SubTaskType == SUBJOB_TYPE_DATACOPY_COPY_PHASE) {
        if (generalInfo.m_backupCopyPhaseStartTime == 0 ||
            (generalInfo.m_restoreCopyIndex != backupSubJob.orderNumberForAggregate)) {
            INFOLOG("need to update general info: %s, %d, %d, %d", backupSubJob.m_ControlFile.c_str(),
                generalInfo.m_backupCopyPhaseStartTime, generalInfo.m_restoreCopyIndex,
                backupSubJob.orderNumberForAggregate);
            return true;
        }
    }

    if (backupSubJob.m_SubTaskType == SUBJOB_TYPE_DATACOPY_DELETE_PHASE) {
        if (generalInfo.m_backupDelPhaseStartTime == 0 ||
            (generalInfo.m_restoreDeleteIndex != backupSubJob.orderNumberForAggregate)) {
            INFOLOG("need to update general info: %s, %d, %d, %d", backupSubJob.m_ControlFile.c_str(),
                generalInfo.m_backupDelPhaseStartTime, generalInfo.m_restoreDeleteIndex,
                backupSubJob.orderNumberForAggregate);
            return true;
        }
    }

    return false;
}

bool HetroCommonService::PrintFinalBackupStats(HetroNativeBackupStats &backupStatistics)
{
    HCP_Log(INFO, MODULE)
        << "\n\n------------------"
        << "\nBackup Statistics"
        << "\n------------------"
        << "\nNo of Dirs Copied          : " << std::to_string(backupStatistics.m_noOfDirCopied)
        << "\nNo of Files Copied         : " << std::to_string(backupStatistics.m_noOfFilesCopied)
        << "\nNo of Bytes Copied         : " << FormatCapacity(backupStatistics.m_noOfBytesCopied)
        << "\nNo of Dir Deleted          : " << std::to_string(backupStatistics.m_noOfDirDeleted)
        << "\nNo of Files Deleted        : " << std::to_string(backupStatistics.m_noOfFilesDeleted)
        << "\nNo of Dir Failed           : " << std::to_string(backupStatistics.m_noOfDirFailed)
        << "\nNo of Files Failed         : " << std::to_string(backupStatistics.m_noOfFilesFailed)
        << "\nNo of Src Retry Count      : " << std::to_string(backupStatistics.m_noOfSrcRetryCount)
        << "\nNo of Dst Retry Count      : " << std::to_string(backupStatistics.m_noOfDstRetryCount)
        << "\nNo of Failed record Written: " << std::to_string(backupStatistics.m_noOfFailureRecordsWritten)
        << "\n\n" << HCPENDLOG;
    return true;
}

bool HetroCommonService::PrintFinalScannerStats(HetroNativeScanStatistics &scanStats)
{
    std::string scanStartTimeStr = FormatTimeToStr(scanStats.m_scanStartTime);
    std::string remoteNasScanEndTimeStr = FormatTimeToStr(scanStats.m_remoteNasScanEndTime);
    std::string scanEndTimeStr = FormatTimeToStr(scanStats.m_scanEndTime);

    int32_t rmtNasScanDur = double(scanStats.m_remoteNasScanEndTime - scanStats.m_scanStartTime);
    int32_t remoteNasScanSpeed = (rmtNasScanDur == 0) ? scanStats.m_totFiles : (scanStats.m_totFiles/rmtNasScanDur);

    int32_t scanDuration = double(scanStats.m_scanEndTime - scanStats.m_scanStartTime);
    int32_t scanSpeed = (scanDuration == 0) ? scanStats.m_totFiles : (scanStats.m_totFiles / scanDuration);

    if (scanStats.m_scanStatus == (int)SCANNER_STATUS::SCAN_COMPLETED) {
        HCP_Log(INFO, MODULE) << "\n\n------------------" << "\nScanner Statistics" << "\n------------------"
            << "\nScan status                : " << "Completed"
            << "\nScan start time            : " << scanStartTimeStr
            << "\nRemote NAS Scan end time   : " << remoteNasScanEndTimeStr
            << "\nRemote NAS Scan duration   : " << std::to_string(rmtNasScanDur) << " sec"
            << "\nRemote NAS Scan speed      : " << std::to_string(remoteNasScanSpeed) << " files/sec"
            << "\nScan duration              : " << std::to_string(scanDuration) << " sec"
            << "\nScan speed                 : " << std::to_string(scanSpeed) << " files/sec"
            << "\nNo of dirs scanned         : " << std::to_string(scanStats.m_totDirs)
            << "\nNo of files scanned        : " << std::to_string(scanStats.m_totFiles)
            << "\nNo of dirs Failed to scan  : " << std::to_string(scanStats.m_totFailedDirs)
            << "\nNo of files Failed to scan : " << std::to_string(scanStats.m_totFailedFiles)
            << "\nTotal Size                 : " << FormatCapacity(scanStats.m_totalSize)
            << "\nNo of dirs to bkup         : " << std::to_string(scanStats.m_totDirsToBackup)
            << "\nNo of files to bkup        : " << std::to_string(scanStats.m_totFilesToBackup)
            << "\nNo of dirs to del          : " << std::to_string(scanStats.m_totDirsDeleted)
            << "\nNo of files to del         : " << std::to_string(scanStats.m_totFilesDeleted)
            << "\nSize to bkup               : " << FormatCapacity(scanStats.m_totalSizeToBackup)
            << "\nNo of control files        : " << std::to_string(scanStats.m_totalControlFiles)
            << "\n\n" << HCPENDLOG;
    } else {
        HCP_Log(INFO, MODULE) << "\n\n------------------" << "\nScanner Statistics" << "\n------------------"
            << "\nScan status               : " << scanStats.m_scanStatus
            << "\nScan start time           : " << scanStartTimeStr
            << "\n# of dirs scanned         : " << std::to_string(scanStats.m_totDirs)
            << "\n# of files scanned        : " << std::to_string(scanStats.m_totFiles)
            << "\n# of dirs Failed to scan  : " << std::to_string(scanStats.m_totFailedDirs)
            << "\n# of files Failed to scan : " << std::to_string(scanStats.m_totFailedFiles)
            << "\nTotal Size                : " << FormatCapacity(scanStats.m_totalSize)
            << "\n# of dirs to bkup         : " << std::to_string(scanStats.m_totDirsToBackup)
            << "\n# of files to bkup        : " << std::to_string(scanStats.m_totFilesToBackup)
            << "\n# of dirs to del          : " << std::to_string(scanStats.m_totDirsDeleted)
            << "\n# of files to del         : " << std::to_string(scanStats.m_totFilesDeleted)
            << "\nSize to bkup              : " << FormatCapacity(scanStats.m_totalSizeToBackup)
            << "\n# control files           : " << std::to_string(scanStats.m_totalControlFiles)
            << "\n\n" << HCPENDLOG;
    }

    return true;
}

void HetroCommonService::PrintBackupScanFilter(ScanDirectoryFilter dirFilter, ScanFileFilter fileFilter)
{
    HCP_Log(INFO, MODULE) << "fileFilter.type:"  << fileFilter.type << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "fileFilter.fileList:"   << HCPENDLOG;
    for (uint16_t i = 0; i < fileFilter.fileList.size(); i++) {
        HCP_Log(INFO, MODULE) << fileFilter.fileList[i] << HCPENDLOG;
    }
    HCP_Log(INFO, MODULE) << "dirFilter.type:" << dirFilter.type << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "scanFilter.directoryFilter.filterList:" << HCPENDLOG;
    for (uint16_t i = 0; i < dirFilter.dirList.size(); i++) {
        HCP_Log(INFO, MODULE) << dirFilter.dirList[i] << HCPENDLOG;
    }
}

int HetroCommonService::ChangeHostnameToIp(std::string& hostname, const std::string& protocol)
{
    if (hostname.empty()) {
        HCP_Log(ERR, MODULE) << "hostname is empty."<< HCPENDLOG;
        return MP_FAILED;
    }
    if (hostname[0] == '[') {
        HCP_Log(DEBUG, MODULE) << "hostname: " << hostname << " is ipv6, no need to change."<< HCPENDLOG;
        return MP_SUCCESS;
    }

    struct addrinfo hints;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_CANONNAME;
    hints.ai_protocol = 0;
    struct addrinfo *addrRet;
    struct addrinfo *addrPtr;

    std::string port = (protocol == NAS_PROTOCOL_TYPE_E_NFS ? NFSPORT : CIFSPORT);

    int ret = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &addrRet);
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Fail to getaddrinfo for host: " << hostname << ", ret = " << ret << HCPENDLOG;
        return MP_FAILED;
    }

    for (addrPtr = addrRet; addrPtr != nullptr; addrPtr = addrPtr->ai_next) {
    char host[NUMBER1024] = {0};
        ret = getnameinfo(addrPtr->ai_addr, addrPtr->ai_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
        if (ret != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "Fail to getnameinfo, ret = " << ret << HCPENDLOG;
            freeaddrinfo(addrRet);
            return MP_FAILED;
        }
        if (hostname == std::string(host)) {
            HCP_Log(DEBUG, MODULE) << "No need to change the host: " << hostname << HCPENDLOG;
            freeaddrinfo(addrRet);
            return MP_SUCCESS;
        }
        if (!HetroCommonService::CheckIpPortConnection(std::string(host), port)) {
            continue;
        }
        hostname = std::string(host);
        freeaddrinfo(addrRet);
        return MP_SUCCESS;
    }
    HCP_Log(ERR, MODULE) << "Fail to getnameinfo, ret = " << ret << ", hostname: " << hostname << HCPENDLOG;
    freeaddrinfo(addrRet);
    return MP_FAILED;
}

bool HetroCommonService::CheckIpPortConnection(const std::string& ip, const std::string& port)
{
    std::vector<Module::CmdParam> cmd {
        CmdParam(COMMON_CMD_NAME, "curl"),
        CmdParam(CMD_OPTION_PARAM, "-ks"),
        CmdParam(CMD_OPTION_PARAM, "-m10"),
        CmdParam(COMMON_PARAM, ip + ":" + port),
    };
    std::vector<std::string> result;
    int retCode = Module::RunCommand("curl", cmd, result);

    retCode = (retCode < (int)RETURNCODEMAX) ? retCode : ntohs(retCode);
    if (retCode != MP_SUCCESS) {
        HCP_Log(INFO, MODULE) << "curl " << ip << ",  retCode: " << retCode << HCPENDLOG;
    }
    if (retCode == RETURNCODE6 || retCode == RETURNCODE7 || retCode == RETURNCODE28) {
        ERRLOG("Exec cmd failed! %d", retCode);
        return false;
    }
    return true;
}

FILTER_TYPE HetroCommonService::GetFilterType(std::string mode)
{
    if (mode == "INCLUDE") {
        return FILTER_TYPE::INCLUDE;
    } else if (mode == "EXCLUDE") {
        return FILTER_TYPE::EXCLUDE;
    } else {
        return FILTER_TYPE::DISABLED;
    }
}

void HetroCommonService::FillScanConfigForFilter(std::vector<AppProtect::ResourceFilter> filters,
    ScanConfig &scanConfig)
{
    ScanDirectoryFilter dirFilter;
    ScanFileFilter fileFilter;
    for (uint16_t i = 0; i < filters.size(); i++) {
        if ((filters[i].filterBy == NAS_FILTER_BY_NAME) && (filters[i].type == NAS_FILTERTYPE_FILE)) {
            fileFilter.type =  GetFilterType(filters[i].mode);
            fileFilter.fileList = filters[i].values;
        } else if ((filters[i].filterBy == NAS_FILTER_BY_NAME) && (filters[i].type == NAS_FILTERTYPE_DIR)) {
            dirFilter.type =  GetFilterType(filters[i].mode);
            dirFilter.dirList = filters[i].values;
        }
    }
    scanConfig.dFilter = dirFilter;
    scanConfig.fFilter = fileFilter;
    HetroCommonService::PrintBackupScanFilter(dirFilter, fileFilter);
    return;
}

bool HetroCommonService::CheckRemoteNasMount(std::string proto, std::string protoVersion,
    ProtectedNasShare &m_nasShare, std::string m_tempLocalMountPath)
{
    std::string nfsShareName = m_nasShare.sharePath;
    if (m_nasShare.nasShareExt.m_protocol == NAS_PROTOCOL_TYPE_E_CIFS && m_nasShare.sharePath[0] == '/') {
        nfsShareName = nfsShareName.substr(1);
    }

    std::string nasProto = proto;
    std::string nasProtoVers = protoVersion;
    std::string nasMountOptions;
    if (m_nasShare.nasShareExt.m_protocol == NAS_PROTOCOL_TYPE_E_CIFS) {
        nasMountOptions = CIFS_MOUNT_OPTION;
    } else {
        nasMountOptions = NFS_MOUNT_OPTION;
        m_nasShare.auth.authkey = "";
        m_nasShare.auth.authPwd = "";
    }
    std::vector<std::string> ipList;
    ipList.push_back(m_nasShare.nasShareExt.m_serviceIP);
    HCP_Log(DEBUG, MODULE) << "m_nasShare.nasShareExt.m_serviceIP: " << m_nasShare.nasShareExt.m_serviceIP << HCPENDLOG;
    NasMountParams mountParams = {
        nasProto, nasProtoVers, nfsShareName, m_tempLocalMountPath, nasMountOptions,
        m_nasShare.auth.authkey, m_nasShare.auth.authPwd, ipList};
    if (PluginUtils::CheckNfsAccessibility(mountParams) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Mount remote nas failed for share: " << WIPE_SENSITIVE(nfsShareName) << HCPENDLOG;
        return false;
    }
    HCP_Log(DEBUG, MODULE) << "Mount remote nas successful" << HCPENDLOG;
    return true;
}

bool HetroCommonService::CheckDataFsMount(std::string proto, std::string protoVersion,
    StorageRepository &m_dataFs, std::string m_dataFsSvcIp, ProtectedNasShare &m_nasShare)
{
    std::string localMountPath = "/mnt/" + m_nasShare.id;
    std::string nfsShareName = m_dataFs.remotePath;
    if (m_dataFs.remotePath.empty()) {
        HCP_Log(ERR, MODULE) << "m_dataFs.remotePath is empty" << HCPENDLOG;
        return false;
    }
    if (m_nasShare.nasShareExt.m_protocol == NAS_PROTOCOL_TYPE_E_CIFS && m_dataFs.remotePath[0] == '/') {
        nfsShareName = nfsShareName.substr(1);
    }

    std::string nasProto = proto;
    std::string nasProtoVers = protoVersion;

    std::string nasMountOptions {};
    if (m_nasShare.nasShareExt.m_protocol == NAS_PROTOCOL_TYPE_E_CIFS) {
        nasMountOptions = CIFS_MOUNT_OPTION;
    } else {
        nasMountOptions = NFS_MOUNT_OPTION;
        m_dataFs.auth.authkey = "";
        m_dataFs.auth.authPwd = "";
    }
    std::vector<std::string> ipList;

    ipList.push_back(m_dataFsSvcIp);
    PluginUtils::NasMountParams mountParams = {
        nasProto, nasProtoVers, nfsShareName, localMountPath,
        nasMountOptions, m_dataFs.auth.authkey, m_dataFs.auth.authPwd, ipList
    };
    if (PluginUtils::CheckNfsAccessibility(mountParams) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Mount Data FS failed for : " << m_nasShare.id << HCPENDLOG;
        return false;
    }

    return true;
}

Module::SmbVersion HetroCommonService::ConvertStringToSmbVersion(std::string versionString)
{
    if (versionString == SMB_VERSION_3_1_1) {
        return Module::SmbVersion::VERSION0311;
    } else if (versionString == SMB_VERSION_3_02) {
        return Module::SmbVersion::VERSION0302;
    } else if (versionString == SMB_VERSION_3_0) {
        return Module::SmbVersion::VERSION0300;
    } else if (versionString == SMB_VERSION_2_1) {
        return Module::SmbVersion::VERSION0210;
    } else if (versionString == SMB_VERSION_2_0) {
        return Module::SmbVersion::VERSION0202;
    } else {
        return Module::SmbVersion::VERSION0300;
    }
}

std::string HetroCommonService::ConvertSmbVersionToString(Module::SmbVersion smbVersion)
{
    std::string versionString;
    switch (smbVersion) {
        case Module::SmbVersion::VERSION0311:
            versionString = SMB_VERSION_3_1_1;
            break;
        case Module::SmbVersion::VERSION0302:
            versionString = SMB_VERSION_3_02;
            break;
        case Module::SmbVersion::VERSION0300:
            versionString = SMB_VERSION_3_0;
            break;
        case Module::SmbVersion::VERSION0210:
            versionString = SMB_VERSION_2_1;
            break;
        case Module::SmbVersion::VERSION0202:
            versionString = SMB_VERSION_2_0;
            break;
        default:
            break;
    }
    return versionString;
}

void HetroCommonService::SetJobCtrlPhase(std::string jobCtrlPhase)
{
    m_jobCtrlPhase = jobCtrlPhase;
    return;
}

bool HetroCommonService::OceanStorV5SnapshotExists(ProtectedNasHost nasHost,
    ProtectedNasShare nasShare, const std::string& snapshotName)
{
    int getRet = MP_FAILED;
    ControlDeviceInfo deviceInfo = GetDeviceInfo(nasHost, nasShare, getRet);
    if (getRet != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Get device info failed." << HCPENDLOG;
        return false;
    }
    deviceInfo.enableProxy = true;

    auto deviceInst = make_unique<OceanstorNas>(deviceInfo);

    string snapshotID {};
    int retCode = deviceInst->QuerySnapshot(snapshotName, snapshotID);
    if (retCode == MP_SUCCESS) {
        HCP_Log(INFO, MODULE) << "Query snapshot found 1 snapshot, ID = " << snapshotID << HCPENDLOG;
        return true;
    } else {
        HCP_Log(INFO, MODULE) << "Query snapshot found 0 snapshot" << HCPENDLOG;
        return false;
    }
}

/**
 * description: Creates a snapshot for the nasHost & nasShare
 * input:
 *  nasHost  - Nas Host (server)
 *  nasShare - Nas Share
 * output:
 *  outSnapshotName - created snapshot name
 *  outSnaphotCreatedTime - time at which the snapshot is created
 *  outSnapshotNasSharePath - NAS Share path to access the created snapshot
 * return: MP_SUCCESS / MP_FAILED
 */
bool HetroCommonService::CreateSnapshot(ProtectedNasHost nasHost, ProtectedNasShare nasShare,
                                        std::string &outSnapshotName, uint64_t &outSnaphotCreatedTime,
                                        std::string &outSnapshotNasSharePath, int64_t &homoErrCode)
{
    int getRet = MP_FAILED;
    Module::ControlDeviceInfo deviceInfo = GetDeviceInfo(nasHost, nasShare, getRet);
    if (getRet != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Get device info failed." << HCPENDLOG;
        return false;
    }

    /* TO-DO - discuss with roshen and add comment */
    deviceInfo.deviceName = (GetVendorType(nasHost.vendorSubType) == Module::STORAGE_ENUM::NETAPP)
        ? ("/" + deviceInfo.deviceName) : deviceInfo.deviceName;

    auto deviceInst = Module::DeviceManager::CreateDeviceInst(deviceInfo, GetVendorType(nasHost.vendorSubType),
        GetProtocolType(nasShare.nasShareExt.m_protocol), false);
    if (deviceInst == nullptr) {
        HCP_Log(ERR, MODULE) << "Create snapshot failed for vendorSubType " << nasHost.vendorSubType << HCPENDLOG;
        return false;
    }

    int errCode = 0;
    std::string errString = "Create snapshot of NAS share Failed";
    std::string currTimeInStr = "";
    outSnaphotCreatedTime = GetCurrentTimeInSeconds(currTimeInStr);
    outSnapshotName = "dp_" + currTimeInStr + "_" + GetUniqueId();

    auto deviceSnap = deviceInst->CreateSnapshot(outSnapshotName, errCode);
    if (deviceSnap == nullptr) {
        homoErrCode = deviceInst->GetErrorCode();
        goto error;
    }

    /**
    * Generally for all vendors, we clone snapshot to a new fs, and take backup from fs
    * Reason is, its possible that customer may configured the protected NAS shre to hide ".snapshot" folder
    * But, Fusion Storage product does not support REST api to clone fs from snapshot.
    * So, only for FusionStorage, we try take backup directly from ".snapshot" folder
    */
    if (NoNeedToCloneFs(nasHost, nasShare)) {
        outSnapshotNasSharePath = nasShare.sharePath + "/.snapshot/" + outSnapshotName;
    } else {
        auto clone = deviceSnap->CreateClone(outSnapshotName, errCode);
        if (clone->GetErrorCode() != 0) {
            currTimeInStr = clone->GetExtendInfo();
            errString = "Create clone of NAS share Failed";
            goto error;
        }
        if (!CloneFsFromSnapshot(nasHost, nasShare, outSnapshotName, homoErrCode)) {
            errString = "Create share of clone Failed";
            goto error;
        }
        outSnapshotNasSharePath = "/" + outSnapshotName;
    }
    return true;

    error:
    ERRLOG("Create snapshot failed for vendorSubType %s, %s", nasHost.vendorSubType.c_str(), errString.c_str());
    if (deviceSnap != nullptr) {
        deviceInst->DeleteSnapshot(outSnapshotName);
    }
    CreateSnapshotError(outSnapshotName, outSnaphotCreatedTime, outSnapshotNasSharePath);
    outSnapshotName = currTimeInStr;
    return false;
}

void HetroCommonService::CreateSnapshotError(std::string &outSnapshotName, uint64_t &outSnaphotCreatedTime,
    std::string &outSnapshotNasSharePath)
{
    outSnapshotName = "";
    outSnaphotCreatedTime = 0;
    outSnapshotNasSharePath = "";
}

bool HetroCommonService::DeleteSnapshot(
    ProtectedNasHost nasHost, ProtectedNasShare nasShare, std::string snapshotName, int64_t &homoErrCode)
{
    if (GetVendorType(nasHost.vendorSubType) == Module::STORAGE_ENUM::FUSIONSTORAGE ||
        (GetVendorType(nasHost.vendorSubType) == Module::STORAGE_ENUM::NETAPP &&
        nasShare.nasShareExt.m_protocol == NAS_PROTOCOL_TYPE_E_NFS)) {
        int getRet;
        Module::ControlDeviceInfo deviceInfo = HetroCommonService::GetDeviceInfo(nasHost, nasShare, getRet);
        if (getRet != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "Get device info failed." << HCPENDLOG;
            return false;
        }
        deviceInfo.enableProxy = true;
        auto deviceInst = Module::DeviceManager::CreateDeviceInst(deviceInfo,
            GetVendorType(nasHost.vendorSubType), GetProtocolType(nasShare.nasShareExt.m_protocol), false);
        if (deviceInst == nullptr) {
            HCP_Log(ERR, MODULE) << "Login & initialization Failed" << HCPENDLOG;
            return false;
        }
        if (deviceInst->DeleteSnapshot(snapshotName) != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << nasHost.vendorSubType << " - Del old Snapshot failed" << HCPENDLOG;
            homoErrCode =  deviceInst->GetErrorCode();
            return false;
        }
        return true;
    }

    if (!DeleteSnapshotAndClonedFs(nasHost, nasShare, snapshotName)) {
        HCP_Log(ERR, MODULE) << nasHost.vendorSubType << " - Delete old snapshot failed" << HCPENDLOG;
        return false;
    }

    return true;
}

bool HetroCommonService::CloneFsFromSnapshot(
    ProtectedNasHost nasHost, ProtectedNasShare nasShare, std::string clonedFsName, int64_t &homoErrCode)
{
    int getRet;
    Module::ControlDeviceInfo deviceInfo = GetDeviceInfo(nasHost, nasShare, getRet);
    if (getRet != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Get device info failed." << HCPENDLOG;
        return false;
    }
    deviceInfo.enableProxy = true;
    deviceInfo.deviceName = clonedFsName;
    auto cloneFs = Module::DeviceManager::CreateDeviceInst(deviceInfo,
        GetVendorType(nasHost.vendorSubType), GetProtocolType(nasShare.nasShareExt.m_protocol), false);
    if (cloneFs == nullptr) {
        HCP_Log(ERR, MODULE) << "Login & initialization of cloned fs Failed" << HCPENDLOG;
        return false;
    }

    Module::ShareParam param;
    param.encryption = (nasShare.nasShareExt.m_encryption == SMB_ENCRYPTION);
    cloneFs->SetShareParam(param);

    int ret = cloneFs->CreateShare();
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "create share of cloned fs Failed" << HCPENDLOG;
        homoErrCode = cloneFs->GetErrorCode();
        return false;
    }

    Module::HostInfo hostInfo;
    hostInfo.hostIp = "*";
    hostInfo.hostId = "";
    std::size_t atPos = nasShare.auth.authkey.find("@");
    if (atPos != std::string::npos) {
        hostInfo.chapAuthName = nasShare.auth.authkey.substr(0, atPos);
        hostInfo.domainName = nasShare.auth.authkey.substr(atPos + 1);
    } else {
        hostInfo.chapAuthName = nasShare.auth.authkey;
        hostInfo.domainName = nasShare.nasShareExt.m_domainName;
    }
    ret = cloneFs->Bind(hostInfo);
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "bind of cloned fs Failed" << HCPENDLOG;
        homoErrCode = cloneFs->GetErrorCode();
        return false;
    }

    return true;
}

bool HetroCommonService::DeleteSnapshotAndClonedFs(
    ProtectedNasHost nasHost, ProtectedNasShare nasShare, std::string snapshotName)
{
    int getRet;
    Module::ControlDeviceInfo deviceInfo = GetDeviceInfo(nasHost, nasShare, getRet);
    if (getRet != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Get device info failed." << HCPENDLOG;
        return false;
    }
    deviceInfo.enableProxy = true;
    deviceInfo.deviceName = snapshotName;
    auto deviceInst = Module::DeviceManager::CreateDeviceInst(deviceInfo,
    GetVendorType(nasHost.vendorSubType), GetProtocolType(nasShare.nasShareExt.m_protocol), false);
    if (deviceInst == nullptr) {
        HCP_Log(ERR, MODULE) << "Login & initialization Failed" << HCPENDLOG;
        return false;
    }

    deviceInst->SetIsDeleteParentSnapShotFlag(true);

    if (deviceInst->Delete() != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << nasHost.vendorSubType << " - Deleting cloned FS & snapshot failed" << HCPENDLOG;
        return false;
    }

    return true;
}

Module::ControlDeviceInfo HetroCommonService::GetDeviceInfo(ProtectedNasHost nasHost,
                                                            ProtectedNasShare nasShare, int& ret)
{
    Module::ControlDeviceInfo deviceInfo;
    deviceInfo.compress = true;
    deviceInfo.dedup = true;
    deviceInfo.url = nasHost.mgrIp;

    deviceInfo.port = to_string(nasHost.port);
    deviceInfo.userName = nasHost.auth.authkey;
    deviceInfo.password = nasHost.auth.authPwd;
    deviceInfo.serviceIp = nasShare.nasShareExt.m_serviceIP;
    deviceInfo.cert = nasHost.certification;
    deviceInfo.crl = nasHost.crl;
    deviceInfo.enableProxy = true;
    if (deviceInfo.serviceIp.empty()) {
        HCP_Log(ERR, MODULE) << "deviceInfo.serviceIp is empty." << HCPENDLOG;
        ret = MP_FAILED;
        return deviceInfo;
    }
    if (ChangeHostnameToIp(deviceInfo.serviceIp, nasShare.nasShareExt.m_protocol) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Fail to change Hostname to ip." << HCPENDLOG;
        ret = MP_FAILED;
        return deviceInfo;
    }
    deviceInfo.poolId = 0;
    deviceInfo.deviceName = nasShare.sharePath.substr(1, nasShare.sharePath.length());
    deviceInfo.nasUserGroup = nasShare.nasShareExt.m_domainName.empty() ? nasShare.auth.authkey
        : (nasShare.nasShareExt.m_domainName + "\\" + nasShare.auth.authkey);
    ret = MP_SUCCESS;
    deviceInfo.shareId = nasShare.nasShareExt.m_shareId;
    deviceInfo.fileSystemId = nasShare.nasShareExt.m_fileSystemId;
    deviceInfo.dtreeId = nasShare.nasShareExt.m_dtreeId;
    deviceInfo.sharePath = nasShare.nasShareExt.m_sharePath;
    return deviceInfo;
}

Module::STORAGE_ENUM HetroCommonService::GetVendorType(const std::string inputVendorStr)
{
    if (inputVendorStr == NAS_VENDOR_TYPE_STR_HUAWEI_OCEANSTOR) {
        return Module::STORAGE_ENUM::OCEANSTOR;
    } else if (inputVendorStr == NAS_VENDOR_TYPE_STR_HUAWEI_DORADO) {
        return Module::STORAGE_ENUM::DORADO;
    } else if (inputVendorStr == NAS_VENDOR_TYPE_STR_HUAWEI_FUSIONSTOR) {
        return Module::STORAGE_ENUM::FUSIONSTORAGE;
    } else if (inputVendorStr == NAS_VENDOR_TYPE_STR_NETAPP_ONTAP9) {
        return Module::STORAGE_ENUM::NETAPP;
    }
    HCP_Log(ERR, MODULE) << "Unknown vendor type: " << inputVendorStr << HCPENDLOG;
    return Module::STORAGE_ENUM::DEFAULT;
}

Module::PROTOCOL HetroCommonService::GetProtocolType(std::string protocol)
{
    if (protocol == NAS_PROTOCOL_TYPE_E_NFS) {
        return Module::PROTOCOL::NFS;
    } else if (protocol == NAS_PROTOCOL_TYPE_E_CIFS) {
        return Module::PROTOCOL::CIFS;
    }
    HCP_Log(ERR, MODULE) << "Unknown protocol type: " << protocol << HCPENDLOG;
    return Module::PROTOCOL::PROTOCOL_DEFAULT;
}

bool HetroCommonService::CertVerifyMgr(const AppProtect::ApplicationEnvironment& protectEnv,
    ProtectedNasHost& nasHost)
{
    HCP_Log(INFO, MODULE) << "Enter certMgr." << HCPENDLOG;
    CertMgr certMgr;
    if (!certMgr.InitInfo(protectEnv, m_jobId)) {
        HCP_Log(ERR, MODULE) << "CertMgr init failed" << HCPENDLOG;
        return false;
    }
    if (!certMgr.IsVerify()) {
        HCP_Log(INFO, MODULE) << "Certificate verification is not required." << HCPENDLOG;
        return true;
    }
    if (!certMgr.FlushCertificationToDisk()) {
        HCP_Log(ERR, MODULE) << "Save cert failed" << HCPENDLOG;
        return false;
    }
    nasHost.certification = certMgr.GetCertPath();
    if (certMgr.IncludeRevocationList()) {
        if (!certMgr.FlushRevocationListToDisk()) {
            HCP_Log(ERR, MODULE) << "Save CRL failed" << HCPENDLOG;
            return false;
        }
        nasHost.crl = certMgr.GetCrlPath();
    }
    return true;
}

bool HetroCommonService::RemoveCertification(const AppProtect::ApplicationEnvironment& protectEnv)
{
    HCP_Log(INFO, MODULE) << "Enter certMgr." << HCPENDLOG;
    CertMgr certMgr;
    if (!certMgr.InitInfo(protectEnv, m_jobId)) {
        HCP_Log(ERR, MODULE) << "CertMgr init failed" << HCPENDLOG;
        return false;
    }
    if (!certMgr.IsVerify()) {
        HCP_Log(INFO, MODULE) << "No certificate needs to be deleted." << HCPENDLOG;
        return true;
    }
    if (!certMgr.RemoveCertAndRevocationListFromDisk()) {
        HCP_Log(ERR, MODULE) << "Remove cert failed" << HCPENDLOG;
        return false;
    }
    return true;
}

void HetroCommonService::SetMainJobId(const string& jobId)
{
    m_jobId = jobId;
}

void HetroCommonService::SetSubJobId()
{
    m_subJobId = GetSubJobId();
}

void HetroCommonService::FillScanConfigForNative(ScanConfig &scanConfig)
{
}

void HetroCommonService::FillScanConfigForAggr(ScanConfig &scanConfig)
{
}

void HetroCommonService::KeepJobAlive(const string& mainJobId, const string& subJobId)
{
    if (mainJobId.empty()) {
        WARNLOG("mainJobId is empty, can not do KeepJobAlive");
        return;
    }
    SubJobDetails subJobDetails {};
    ActionResult result {};
    subJobDetails.__set_jobId(mainJobId);
    if (!subJobId.empty()) {
        subJobDetails.__set_subJobId(subJobId);
    }
    subJobDetails.__set_jobStatus(SubJobStatus::RUNNING);
    INFOLOG("Enter ReportJobDetails: jobId: %s, subJobId: %s, jobStatus: RUNNING", mainJobId.c_str(), subJobId.c_str());
    int retryCnt = 0;
    do {
        JobService::ReportJobDetails(result, subJobDetails);
        if (result.code == Module::SUCCESS) {
            HCP_Log(DEBUG, MODULE) << "Exit ReportJobDetails: "
                << "jobId: " << mainJobId
                << ", subJobId: " << subJobId
                << ", jobStatus: RUNNING" << HCPENDLOG;
            return;
        }
        WARNLOG("keep job alive failed, retryCnt: %d, code: %d, jobId: %s", retryCnt, result.code, mainJobId.c_str());
        sleep(NUMBER4);
    } while (++retryCnt <= NUMBER5);
    ERRLOG("keep job alive failed after max retry, jobId: %s, subJobId: %s", mainJobId.c_str(), subJobId.c_str());
}

bool HetroCommonService::SendJobReportForAliveness()
{
    int64_t currTime = GetCurrentTimeInSeconds();
    if ((currTime - m_lastJobReportTime) > REPORT_INTERVAL) {
        KeepJobAlive(m_jobId, m_subJobId);
        m_lastJobReportTime = GetCurrentTimeInSeconds();
    }
    return true;
}

void HetroCommonService::SleepForCreateSubTaskError()
{
    uint32_t count = 0;
    do {
        if (IsAbortJob()) {
            WARNLOG("Exit received Abort for taskid: %s, subtaskid: %s", m_jobId.c_str(), m_subJobId.c_str());
            break;
        }
        sleep(NUMBER60);
        SendJobReportForAliveness();
    } while (count++ < NUMBER3);
}

bool HetroCommonService::CheckAndRetryCreateSubTask(std::vector<SubJob> &subJobList,
    std::vector<std::string> &ctrlFileList, int64_t &createJobErrTime, bool isFinal)
{
    if (subJobList.empty() || ctrlFileList.empty()) {
        DBGLOG("subJobList or ctrlFileList are empty, exit CreateSubTask");
        return true;
    }
    DBGLOG("Enter with subJobList size = %zu, ctrlFileList.size()=%zu", subJobList.size(), ctrlFileList.size());
    do {
        if (IsAbortJob()) {
            WARNLOG("Exit received Abort for taskid: %s, subtaskid: %s", m_jobId.c_str(), m_subJobId.c_str());
            break;
        }
        int ret = CreateSubTask(subJobList, ctrlFileList);
        if (ret == Module::SUCCESS) {
            HCP_Log(DEBUG, MODULE) << "Success to create subJobList." << HCPENDLOG;
            break;
        }
        if (ret == Module::RETRY) {
            HCP_Log(WARN, MODULE) << "Create subtask failed with retriable error"<< HCPENDLOG;
            createJobErrTime = GetCurrentTimeInSeconds();
            // is final false means still scanning , true means scan finish.
            // if still scanning, can return here, if scan finish , has to create subtask here, can not return.
            if (!isFinal) {
                subJobList.clear();
                ctrlFileList.clear();
                return true;
            } else {
                HCP_Log(WARN, MODULE) << "sleep for create subtask error"<< HCPENDLOG;
                SleepForCreateSubTaskError();
                continue;
            }
        } else {
            HCP_Log(ERR, MODULE) << "Exit CreateSubTasksFromCtrlFile, Create subtask failed" << HCPENDLOG;
            return false;
        }
    } while (true);
    return true;
}

bool HetroCommonService::CreateBackupJobTaskToCreateFurtherSubTasks()
{
    std::string backupSubJobStr {};
    HetroBackupSubJob backupSubJob {};
    SubJob subJob {};
    backupSubJob.m_SubTaskType = SUBJOB_TYPE_CREATE_SUBJOB_PHASE;
    backupSubJob.orderNumberForAggregate = m_orderNumberForAggregate;

    if (!Module::JsonHelper::StructToJsonString(backupSubJob, backupSubJobStr)) {
        HCP_Log(ERR, MODULE) << "Exit CreateBackupJobTaskToCreateFurtherSubTasks failed" << HCPENDLOG;
        return false;
    }
    subJob.__set_jobId(m_jobId);
    subJob.__set_jobType(SubJobType::BUSINESS_SUB_JOB);
    subJob.__set_policy(ExecutePolicy::ANY_NODE);
    subJob.__set_jobInfo(backupSubJobStr);
    subJob.__set_ignoreFailed(false);

    std::string createSubJobName = SUBJOB_TYPE_CREATE_SUBJOB_JOBNAME + m_jobId;
    uint32_t subTaskPrio = SUBJOB_TYPE_CREATE_SUBJOB_PHASE_PRIO;
    if (m_aggregateRestore) {
        subTaskPrio = m_orderNumberForAggregate * SUBJOB_TYPE_AGGREGATE_BASE_PRIO + subTaskPrio;
        createSubJobName += to_string(subTaskPrio);
    }
    subJob.__set_jobPriority(subTaskPrio);
    subJob.__set_jobName(createSubJobName);

    do {
        if (IsAbortJob()) {
            WARNLOG("Exit received Abort for taskid: %s, subtaskid: %s", m_jobId.c_str(), m_subJobId.c_str());
            break;
        }
        int ret = CreateSubTask(subJob);
        if (ret == Module::SUCCESS) {
            break;
        } else if (ret == Module::RETRY) {
            HCP_Log(WARN, MODULE) << "Create subtask failed with retriable error"<< HCPENDLOG;
            SleepForCreateSubTaskError();
            continue;
        } else {
            ERRLOG("Exit CreateBackupJobTaskToCreateFurtherSubTasks, CreateSubtask failed");
            return false;
        }
    } while (true);
    return true;
}

void HetroCommonService::MergeBackupFailureRecords(const std::string& src_output_failure_record_dir)
{
    try {
        INFOLOG("start to merge failure records, output directory root: path %s, jobID: %s, src_output_failure_record_dir: %s",
            m_failureRecordRoot.c_str(), m_jobId.c_str(), src_output_failure_record_dir.c_str());
        if (!boost::filesystem::exists(m_failureRecordRoot) || !boost::filesystem::is_directory(m_failureRecordRoot)) {
            ERRLOG("failed to merge failure records, dir %s not exists", m_failureRecordRoot.c_str());
            return;
        }
        if (!Module::BackupFailureRecorder::Merge(m_failureRecordRoot, m_jobId, src_output_failure_record_dir)) {
            ERRLOG("merge backup failure records failed");
        }
    } catch (const std::exception& e) {
        ERRLOG("merge backup failure records failed with exception: %s", e.what());
    }
}

void HetroCommonService::ReportJobDetailsWithLabelAndErrcode(
    const std::tuple<JobLogLevel::type, SubJobStatus::type, const int>& reportInfo,
    const std::string& logLabel, const int64_t errCode, const std::string& message)
{
    SubJobDetails subJobDetails;
    LogDetail logDetail {};
    logDetail.__set_additionalDesc(vector<string>{message});
    std::vector<LogDetail> logDetailList;
    ActionResult result;
    int jobSpeed = 0;
    SubJobStatus::type jobStatus = std::get<1>(reportInfo);
    AddLogDetail(logDetail, logLabel, std::get<0>(reportInfo));
    int jobProgress = std::get<2>(reportInfo);
    AddErrCode(logDetail, errCode);
    DBGLOG("Enter ReportJobDetails: jobId: %s, subJobId: %s, jobStatus: %d, logLabel: %s, errCode: %d, message：%s",
        m_jobId.c_str(), m_subJobId.c_str(), static_cast<int>(jobStatus), logLabel.c_str(), errCode, message.c_str());

    REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, jobProgress, jobSpeed, jobStatus);
    if (result.code != Module::SUCCESS) {
        ERRLOG("ReportJobDetails failed, jobId: %s, subJobId: %s, errCode: %d",
            m_jobId.c_str(), m_subJobId.c_str(), result.code);
    }

    DBGLOG("Exit ReportJobDetails: jobId: %s, subJobId: %s, jobStatus: %d, logLabel: %s, errCode: %d",
        m_jobId.c_str(), m_subJobId.c_str(), static_cast<int>(jobStatus), logLabel.c_str(), errCode);
    return;
}

inline bool HetroCommonService::NoNeedToCloneFs(const ProtectedNasHost &nasHost, const ProtectedNasShare &nasShare)
{
    return (GetVendorType(nasHost.vendorSubType) == Module::STORAGE_ENUM::FUSIONSTORAGE ||
        (GetVendorType(nasHost.vendorSubType) == Module::STORAGE_ENUM::NETAPP &&
        nasShare.nasShareExt.m_protocol == NAS_PROTOCOL_TYPE_E_NFS));
}

void HetroCommonService::RegisterScanTask()
{
    int times = 0;
    while (!m_scanMgr.RegisterScanTask(NAS_SCANNER_MAX_SIZE) && !IsAbortJob()) {
        Module::SleepFor(std::chrono::seconds(NUMBER10));
        //  每150秒打印一次
        ++times;
        if (times == NUMBER15) {
            times = 0;
            INFOLOG("Current scanner size: %d", m_scanMgr.GetTaskSize());
        }
    }
}
void HetroCommonService::ReleaseScanTask()
{
    m_scanMgr.ReleaseScanTask();
}

void HetroCommonService::RemoveSubJobFromChannelMgr()
{
    string jobId = GetParentJobId();
    string subJobId = GetSubJobId();
    ChannelManager::GetInstance().RemoveSubJob(jobId, subJobId);
}

void HetroCommonService::AddSubJobToChannelMgr()
{
    string jobId = GetParentJobId();
    string subJobId = GetSubJobId();
    ChannelManager::GetInstance().AddSubJob(jobId, subJobId);
}